import passport from "passport";
import { Strategy as GoogleStrategy } from "passport-google-oauth20";
import { Strategy as LinkedInStrategy } from "passport-linkedin-oauth2";
import { Strategy as GitHubStrategy } from "passport-github2";
import database from "../config/db";

// Google Strategy
passport.use(
  new GoogleStrategy(
    {
      clientID: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      callbackURL: `${process.env.SERVER_URL}/api/v1/social/google/callback`,
      scope: ["profile", "email"],
    },
    async (accessToken, refreshToken, profile, done) => {
      try {
        const email = profile.emails?.[0]?.value;
        const name = profile.displayName;
        const googleId = profile.id;

        if (!email) {
          return done(new Error("No email found in Google profile"), undefined);
        }

        // Check if account already exists with this Google ID
        let account = await database.query(
          `SELECT * FROM accounts WHERE google_id = $1`,
          [googleId],
        );

        if (account.rows.length > 0) {
          return done(null, account.rows[0]);
        }

        // Check if account exists with this email
        account = await database.query(
          `SELECT * FROM accounts WHERE email = $1`,
          [email],
        );

        if (account.rows.length > 0) {
          // Update existing account with Google ID
          await database.query(
            `UPDATE accounts SET 
              google_id = $1,
              social_auth_providers = array_append(COALESCE(social_auth_providers, '{}'), 'google'),
              updated_at = CURRENT_TIMESTAMP
            WHERE id = $2`,
            [googleId, account.rows[0].id],
          );
          return done(null, account.rows[0]);
        }

        // Create new account
        const username =
          email.split("@")[0] +
          "_" +
          Math.random().toString(36).substring(2, 7);

        const newAccount = await database.query(
          `INSERT INTO accounts (
            account_type,
            username,
            email,
            google_id,
            display_name,
            user_full_name,
            is_email_verified,
            email_verified_at,
            social_auth_providers,
            account_status,
            account_created_via
          ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) 
          RETURNING *`,
          [
            "user",
            username,
            email,
            googleId,
            name,
            name,
            true,
            new Date(),
            ["google"],
            "active",
            "google",
          ],
        );

        done(null, newAccount.rows[0]);
      } catch (error) {
        done(error as Error, undefined);
      }
    },
  ),
);

// GitHub Strategy
interface GitHubProfile {
  id: number;
  username: string;
  displayName?: string;
  profileUrl: string;
  emails?: Array<{ value: string }>;
}

interface AccountRow {
  id: string;
  [key: string]: any;
}
  
interface QueryResult {
  rows: AccountRow[];
}

passport.use(
  new GitHubStrategy(
    {
      clientID: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
      callbackURL: `${process.env.SERVER_URL}/api/v1/social/github/callback`,
      scope: ["user:email"],
    },
    async (
      accessToken: string,
      refreshToken: string | undefined,
      profile: GitHubProfile,
      done: (err: Error | null, user?: AccountRow) => void,
    ) => {
      try {
        const email: string =
          profile.emails?.[0]?.value ||
          `${profile.username}@users.noreply.github.com`;
        const name: string = profile.displayName || profile.username;
        const githubId: string = profile.id.toString();

        // Check if account already exists with this GitHub ID
        let account: QueryResult = await database.query(
          `SELECT * FROM accounts WHERE github_id = $1`,
          [githubId],
        );

        if (account.rows.length > 0) {
          return done(null, account.rows[0]);
        }

        // Check if account exists with this email
        account = await database.query(
          `SELECT * FROM accounts WHERE email = $1`,
          [email],
        );

        if (account.rows.length > 0) {
          // Update existing account with GitHub ID
          await database.query(
            `UPDATE accounts SET 
              github_id = $1,
              social_auth_providers = array_append(COALESCE(social_auth_providers, '{}'), 'github'),
              updated_at = CURRENT_TIMESTAMP
            WHERE id = $2`,
            [githubId, account.rows[0].id],
          );
          return done(null, account.rows[0]);
        }

        // Create new account
        const username: string =
          profile.username + "_" + Math.random().toString(36).substring(2, 7);

        const newAccount: QueryResult = await database.query(
          `INSERT INTO accounts (
            account_type,
            username,
            email,
            github_id,
            display_name,
            user_full_name,
            is_email_verified,
            email_verified_at,
            social_auth_providers,
            account_status,
            account_created_via,
            user_github_url
          ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) 
          RETURNING *`,
          [
            "user",
            username,
            email,
            githubId,
            name,
            name,
            true,
            new Date(),
            ["github"],
            "active",
            "github",
            profile.profileUrl,
          ],
        );

        done(null, newAccount.rows[0]);
      } catch (error) {
        done(error as Error, undefined);
      }
    },
  ),
);

// LinkedIn Strategy
passport.use(
  new LinkedInStrategy(
    {
      clientID: process.env.LINKEDIN_CLIENT_ID!,
      clientSecret: process.env.LINKEDIN_CLIENT_SECRET!,
      callbackURL: `${process.env.SERVER_URL}/api/v1/social/linkedin/callback`,
      // scope: ['r_emailaddress', 'r_liteprofile'], // Basic profile and email
      scope: ["openid", "profile"], // Updated scopes as per LinkedIn's new requirements
      // state: true, // Enable state parameter for CSRF protection
      // passReqToCallback: false,
    },
    async (
      accessToken: string,
      refreshToken: string | undefined,
      profile: any,
      done: (err: Error | null, user?: AccountRow) => void,
    ) => {
      try {
        // LinkedIn profile structure
        const email = profile.emails?.[0]?.value;
        const name = profile.displayName;
        const firstName = profile.name?.givenName || "";
        const lastName = profile.name?.familyName || "";
        const linkedinId = profile.id;
        const profileUrl =
          profile._json.profileUrl ||
          `https://www.linkedin.com/in/${profile.id}/`;
        const profilePhoto = profile.photos?.[0]?.value;

        if (!email) {
          return done(
            new Error("No email found in LinkedIn profile"),
            undefined,
          );
        }

        // Check if account already exists with this LinkedIn ID
        let account = await database.query(
          `SELECT * FROM accounts WHERE linkedin_id = $1`,
          [linkedinId],
        );

        if (account.rows.length > 0) {
          return done(null, account.rows[0]);
        }

        // Check if account exists with this email
        account = await database.query(
          `SELECT * FROM accounts WHERE email = $1`,
          [email],
        );

        if (account.rows.length > 0) {
          // Update existing account with LinkedIn ID
          await database.query(
            `UPDATE accounts SET 
              linkedin_id = $1,
              social_auth_providers = array_append(COALESCE(social_auth_providers, '{}'), 'linkedin'),
              updated_at = CURRENT_TIMESTAMP,
              user_profile_photo = COALESCE(user_profile_photo, $2)
            WHERE id = $3`,
            [linkedinId, profilePhoto, account.rows[0].id],
          );
          return done(null, account.rows[0]);
        }

        // Create new account
        const username =
          email.split("@")[0] +
          "_" +
          Math.random().toString(36).substring(2, 7);
        const fullName = `${firstName} ${lastName}`.trim() || name;

        const newAccount = await database.query(
          `INSERT INTO accounts (
            account_type,
            username,
            email,
            linkedin_id,
            display_name,
            user_full_name,
            first_name,
            last_name,
            is_email_verified,
            email_verified_at,
            social_auth_providers,
            account_status,
            account_created_via,
            user_profile_photo,
            user_linkedin_url
          ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) 
          RETURNING *`,
          [
            "user",
            username,
            email,
            linkedinId,
            name,
            fullName,
            firstName,
            lastName,
            true,
            new Date(),
            ["linkedin"],
            "active",
            "linkedin",
            profilePhoto,
            profileUrl,
          ],
        );

        done(null, newAccount.rows[0]);
      } catch (error) {
        console.error("LinkedIn OAuth error:", error);
        done(error as Error, undefined);
      }
    },
  ),
);

// Serialize user
passport.serializeUser((user: any, done) => {
  done(null, user.id);
});

// Deserialize user
passport.deserializeUser(async (id: string, done) => {
  try {
    const result = await database.query(
      `SELECT * FROM accounts WHERE id = $1`,
      [id],
    );

    if (result.rows.length > 0) {
      done(null, result.rows[0]);
    } else {
      done(new Error("User not found"), null);
    }
  } catch (error) {
    done(error, null);
  }
});

export default passport;
