import { Request, Response, NextFunction } from "express";
import { catchAsyncError } from "../../middlewares/catchAsyncError";
import { uploadFile, deleteFile } from "../../utils/cloudStorage";
import ErrorHandler from "../../middlewares/error";
import database from "../../config/db";
import bcrypt from "bcrypt";

// Helper function to remove sensitive data
const sanitizeAccount = (account: any) => {
  const {
    password_hash,
    reset_password_token,
    reset_password_expires_at,
    otp_code,
    otp_expires_at,
    ...sanitized
  } = account;
  return sanitized;
};

// controller: get current user profile
export const getMyProfile = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const account = (req as any).user;

    // Get complete profile data
    const result = await database.query(
      `SELECT * FROM accounts WHERE id = $1`,
      [account.id],
    );

    if (result.rows.length === 0) {
      return next(new ErrorHandler("Account not found.", 404));
    }

    const fullProfile = result.rows[0];

    res.status(200).json({
      success: true,
      data: {
        profile: sanitizeAccount(fullProfile),
      },
    });
  },
);

// controller: get public profile by username or ID
export const getPublicProfile = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const { identifier } = req.params;

    if (!identifier) {
      return next(new ErrorHandler("Profile identifier is required.", 400));
    }

    // Check if identifier is UUID or username
    const isUUID =
      /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
        identifier,
      );
    const queryField = isUUID ? "id" : "username";

    const result = await database.query(
      `SELECT * FROM accounts WHERE ${queryField} = $1 AND is_active = true AND account_status = 'active'`,
      [identifier],
    );

    if (result.rows.length === 0) {
      return next(new ErrorHandler("Profile not found.", 404));
    }

    const profile = result.rows[0];

    // Check privacy settings
    if (!profile.is_public) {
      return next(new ErrorHandler("This profile is private.", 403));
    }

    // Prepare public profile data (exclude private information based on privacy settings)
    const publicProfile = {
      id: profile.id,
      account_type: profile.account_type,
      username: profile.username,
      display_name: profile.display_name,
      avatar: profile.avatar,
      cover_image: profile.cover_image,
      bio: profile.bio,
      website: profile.website,
      location_city: profile.location_city,
      location_country: profile.location_country,
      created_at: profile.created_at,
      profile_completion_percentage: profile.profile_completion_percentage,

      // User specific public fields
      ...(profile.account_type === "user" && {
        user_full_name:
          profile.privacy_settings?.profile_visibility === "public"
            ? profile.user_full_name
            : null,
        user_job_title: profile.user_job_title,
        user_current_company: profile.user_current_company,
        user_skills: profile.user_skills,
        user_experience_years: profile.user_experience_years,
        user_github_url: profile.user_github_url,
        user_linkedin_url: profile.user_linkedin_url,
        user_twitter_url: profile.user_twitter_url,
      }),

      // Company specific public fields
      ...(profile.account_type === "company" && {
        company_name: profile.company_name,
        company_size: profile.company_size,
        company_description: profile.company_description,
        company_website: profile.company_website,
      }),
    };

    res.status(200).json({
      success: true,
      data: {
        profile: publicProfile,
      },
    });
  },
);

// controller: update profile
export const updateProfile = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const account = (req as any).user;
    const updateData = req.body;

    // Remove fields that cannot be updated directly
    const disallowedFields = [
      "id",
      "username",
      "email",
      "password_hash",
      "is_email_verified",
      "email_verified_at",
      "google_id",
      "github_id",
      "account_status",
      "created_at",
      "updated_at",
      "deleted_at",
    ];

    Object.keys(updateData).forEach((key) => {
      if (disallowedFields.includes(key)) {
        delete updateData[key];
      }
    });

    // Account type specific validation
    if (account.account_type === "user") {
      // Ensure user-specific fields are not set for company accounts
      if (updateData.company_name || updateData.company_description) {
        return next(
          new ErrorHandler(
            "Company fields are not allowed for user accounts.",
            400,
          ),
        );
      }
    } else if (account.account_type === "company") {
      // Ensure user-specific fields are not set for company accounts
      if (updateData.user_full_name || updateData.user_job_title) {
        return next(
          new ErrorHandler(
            "User fields are not allowed for company accounts.",
            400,
          ),
        );
      }
    }

    // Prepare update query dynamically
    const setClauses: string[] = [];
    const values: any[] = [];
    let paramCounter = 1;

    Object.entries(updateData).forEach(([key, value]) => {
      setClauses.push(`${key} = $${paramCounter}`);
      values.push(value);
      paramCounter++;
    });

    if (setClauses.length === 0) {
      return next(new ErrorHandler("No valid fields to update.", 400));
    }

    // Add updated_at timestamp
    setClauses.push(`updated_at = CURRENT_TIMESTAMP`);

    // Execute update
    const query = `
    UPDATE accounts 
    SET ${setClauses.join(", ")}
    WHERE id = $${paramCounter}
    RETURNING *
  `;

    values.push(account.id);

    const result = await database.query(query, values);

    const updatedAccount = result.rows[0];

    res.status(200).json({
      success: true,
      message: "Profile updated successfully.",
      data: {
        profile: sanitizeAccount(updatedAccount),
      },
    });
  },
);

// controller: update avatar
export const updateAvatar = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const account = (req as any).user;

    if (!req.files || !req.files.avatar) {
      return next(new ErrorHandler("Avatar file is required.", 400));
    }

    const avatarFile = Array.isArray(req.files.avatar)
      ? req.files.avatar[0]
      : req.files.avatar;

    // Validate file type
    const allowedTypes = [
      "image/jpeg",
      "image/jpg",
      "image/png",
      "image/webp",
      "image/gif",
    ];
    if (!allowedTypes.includes(avatarFile.mimetype)) {
      return next(
        new ErrorHandler(
          "Only JPEG, PNG, WebP, and GIF images are allowed.",
          400,
        ),
      );
    }

    // Validate file size (max 5MB)
    const maxSize = 5 * 1024 * 1024; // 5MB
    if (avatarFile.size > maxSize) {
      return next(new ErrorHandler("Avatar image must be less than 5MB.", 400));
    }

    // Delete old avatar if exists
    const currentAccount = await database.query(
      `SELECT avatar FROM accounts WHERE id = $1`,
      [account.id],
    );

    if (currentAccount.rows[0]?.avatar?.url) {
      try {
        await deleteFile(currentAccount.rows[0].avatar.url);
      } catch (error) {
        console.error("Failed to delete old avatar:", error);
      }
    }

    // Upload new avatar
    const uploadResult = await uploadFile(avatarFile, "avatars", account.id);

    // Update account with new avatar
    const result = await database.query(
      `UPDATE accounts 
     SET avatar = $1, updated_at = CURRENT_TIMESTAMP
     WHERE id = $2
     RETURNING *`,
      [uploadResult, account.id],
    );

    const updatedAccount = result.rows[0];

    res.status(200).json({
      success: true,
      message: "Avatar updated successfully.",
      data: {
        avatar: updatedAccount.avatar,
      },
    });
  },
);

// controller: update cover image
export const updateCoverImage = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const account = (req as any).user;

    if (!req.files || !req.files.cover_image) {
      return next(new ErrorHandler("Cover image file is required.", 400));
    }

    const coverFile = Array.isArray(req.files.cover_image)
      ? req.files.cover_image[0]
      : req.files.cover_image;

    // Validate file type
    const allowedTypes = ["image/jpeg", "image/jpg", "image/png", "image/webp"];
    if (!allowedTypes.includes(coverFile.mimetype)) {
      return next(
        new ErrorHandler("Only JPEG, PNG, and WebP images are allowed.", 400),
      );
    }

    // Validate file size (max 10MB)
    const maxSize = 10 * 1024 * 1024; // 10MB
    if (coverFile.size > maxSize) {
      return next(new ErrorHandler("Cover image must be less than 10MB.", 400));
    }

    // Delete old cover image if exists
    const currentAccount = await database.query(
      `SELECT cover_image FROM accounts WHERE id = $1`,
      [account.id],
    );

    if (currentAccount.rows[0]?.cover_image?.url) {
      try {
        await deleteFile(currentAccount.rows[0].cover_image.url);
      } catch (error) {
        console.error("Failed to delete old cover image:", error);
      }
    }

    // Upload new cover image
    const uploadResult = await uploadFile(
      coverFile,
      "cover-images",
      account.id,
    );

    // Update account with new cover image
    const result = await database.query(
      `UPDATE accounts 
     SET cover_image = $1, updated_at = CURRENT_TIMESTAMP
     WHERE id = $2
     RETURNING *`,
      [uploadResult, account.id],
    );

    const updatedAccount = result.rows[0];

    res.status(200).json({
      success: true,
      message: "Cover image updated successfully.",
      data: {
        cover_image: updatedAccount.cover_image,
      },
    });
  },
);

// controller: remove avatar
export const removeAvatar = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const account = (req as any).user;

    // Get current avatar
    const currentAccount = await database.query(
      `SELECT avatar FROM accounts WHERE id = $1`,
      [account.id],
    );

    const currentAvatar = currentAccount.rows[0]?.avatar;

    if (!currentAvatar || !currentAvatar.url) {
      return next(new ErrorHandler("No avatar to remove.", 400));
    }

    // Delete file from storage
    try {
      await deleteFile(currentAvatar.url);
    } catch (error) {
      console.error("Failed to delete avatar file:", error);
    }

    // Update account
    const result = await database.query(
      `UPDATE accounts 
     SET avatar = NULL, updated_at = CURRENT_TIMESTAMP
     WHERE id = $1
     RETURNING *`,
      [account.id],
    );

    const updatedAccount = result.rows[0];

    res.status(200).json({
      success: true,
      message: "Avatar removed successfully.",
      data: {
        profile: sanitizeAccount(updatedAccount),
      },
    });
  },
);

// controller: remove cover image
export const removeCoverImage = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const account = (req as any).user;

    // Get current cover image
    const currentAccount = await database.query(
      `SELECT cover_image FROM accounts WHERE id = $1`,
      [account.id],
    );

    const currentCover = currentAccount.rows[0]?.cover_image;

    if (!currentCover || !currentCover.url) {
      return next(new ErrorHandler("No cover image to remove.", 400));
    }

    // Delete file from storage
    try {
      await deleteFile(currentCover.url);
    } catch (error) {
      console.error("Failed to delete cover image:", error);
    }

    // Update account
    const result = await database.query(
      `UPDATE accounts 
     SET cover_image = NULL, updated_at = CURRENT_TIMESTAMP
     WHERE id = $1
     RETURNING *`,
      [account.id],
    );

    const updatedAccount = result.rows[0];

    res.status(200).json({
      success: true,
      message: "Cover image removed successfully.",
      data: {
        profile: sanitizeAccount(updatedAccount),
      },
    });
  },
);

// controller: update account settings
export const updateAccountSettings = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const account = (req as any).user;
    const {
      is_public,
      email_notifications,
      push_notifications,
      language_preference,
      timezone,
      privacy_settings,
    } = req.body;

    const updates: any = {};

    if (is_public !== undefined) updates.is_public = is_public;
    if (email_notifications !== undefined)
      updates.email_notifications = email_notifications;
    if (push_notifications !== undefined)
      updates.push_notifications = push_notifications;
    if (language_preference !== undefined)
      updates.language_preference = language_preference;
    if (timezone !== undefined) updates.timezone = timezone;
    if (privacy_settings !== undefined)
      updates.privacy_settings = privacy_settings;

    if (Object.keys(updates).length === 0) {
      return next(new ErrorHandler("No settings provided to update.", 400));
    }

    // Prepare update query
    const setClauses: string[] = [];
    const values: any[] = [];
    let paramCounter = 1;

    Object.entries(updates).forEach(([key, value]) => {
      setClauses.push(`${key} = $${paramCounter}`);

      // Handle JSON fields
      if (typeof value === "object") {
        values.push(JSON.stringify(value));
      } else {
        values.push(value);
      }

      paramCounter++;
    });

    setClauses.push(`updated_at = CURRENT_TIMESTAMP`);

    const query = `
    UPDATE accounts 
    SET ${setClauses.join(", ")}
    WHERE id = $${paramCounter}
    RETURNING *
  `;

    values.push(account.id);

    const result = await database.query(query, values);

    const updatedAccount = result.rows[0];

    res.status(200).json({
      success: true,
      message: "Account settings updated successfully.",
      data: {
        settings: {
          is_public: updatedAccount.is_public,
          email_notifications: updatedAccount.email_notifications,
          push_notifications: updatedAccount.push_notifications,
          language_preference: updatedAccount.language_preference,
          timezone: updatedAccount.timezone,
          privacy_settings: updatedAccount.privacy_settings,
        },
      },
    });
  },
);

// controller: deactivate account
export const deactivateAccount = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const account = (req as any).user;
    const { reason } = req.body;

    // Update account status
    await database.query(
      `UPDATE accounts 
     SET 
       account_status = 'deactivated',
       is_active = false,
       updated_at = CURRENT_TIMESTAMP
     WHERE id = $1`,
      [account.id],
    );

    // Optionally log the deactivation reason
    if (reason) {
      // You could create a separate audit log table here
      console.log(`Account ${account.id} deactivated. Reason: ${reason}`);
    }

    // Clear cookies
    res.clearCookie("access_token");
    res.clearCookie("refresh_token");

    res.status(200).json({
      success: true,
      message: "Account deactivated successfully.",
    });
  },
);

// controller: reactivate account
export const reactivateAccount = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const { email } = req.body;

    if (!email) {
      return next(new ErrorHandler("Email is required.", 400));
    }

    const result = await database.query(
      `SELECT * FROM accounts WHERE email = $1 AND account_status = 'deactivated'`,
      [email],
    );

    if (result.rows.length === 0) {
      return next(
        new ErrorHandler("No deactivated account found with this email.", 404),
      );
    }

    const account = result.rows[0];

    // Update account status
    await database.query(
      `UPDATE accounts 
     SET 
       account_status = 'active',
       is_active = true,
       updated_at = CURRENT_TIMESTAMP
     WHERE id = $1`,
      [account.id],
    );

    // Generate OTP for verification
    const otp = Math.floor(100000 + Math.random() * 900000).toString();
    const otpExpireTime = Date.now() + 5 * 60 * 1000;

    await database.query(
      `UPDATE accounts SET 
      otp_code = $1,
      otp_expires_at = to_timestamp($2)
    WHERE id = $3`,
      [otp, otpExpireTime / 1000, account.id],
    );

    res.status(200).json({
      success: true,
      message:
        "Account reactivation initiated. Please check your email for OTP.",
      data: {
        account_id: account.id,
        email: account.email,
      },
    });
  },
);

// controller: upgrade user to company account
export const upgradeToCompany = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const account = (req as any).user;
    const { company_name, company_description, company_website } = req.body;

    if (account.account_type !== "user") {
      return next(
        new ErrorHandler(
          "Only user accounts can be upgraded to company accounts.",
          400,
        ),
      );
    }

    if (!company_name) {
      return next(new ErrorHandler("Company name is required.", 400));
    }

    // Check if email is verified
    if (!account.is_email_verified) {
      return next(
        new ErrorHandler(
          "Email must be verified before upgrading to company account.",
          400,
        ),
      );
    }

    // Check if company name is already taken
    const existingCompany = await database.query(
      `SELECT id FROM accounts WHERE company_name = $1 AND account_type = 'company'`,
      [company_name],
    );

    if (existingCompany.rows.length > 0) {
      return next(new ErrorHandler("Company name is already taken.", 400));
    }

    // Call the database function to upgrade
    const upgradeResult = await database.query(
      `SELECT * FROM upgrade_user_to_company($1, $2, $3)`,
      [account.id, company_name, company_description || null],
    );

    const result = upgradeResult.rows[0];

    if (!result.success) {
      return next(new ErrorHandler(result.message, 400));
    }

    // Update additional company fields if provided
    if (company_website) {
      await database.query(
        `UPDATE accounts SET company_website = $1 WHERE id = $2`,
        [company_website, account.id],
      );
    }

    // Get updated account
    const updatedAccountResult = await database.query(
      `SELECT * FROM accounts WHERE id = $1`,
      [account.id],
    );

    const updatedAccount = updatedAccountResult.rows[0];

    res.status(200).json({
      success: true,
      message: "Account upgraded to company successfully.",
      data: {
        account: sanitizeAccount(updatedAccount),
      },
    });
  },
);

// controller: search accounts
export const searchAccounts = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const {
      query,
      account_type,
      location_country,
      location_city,
      company_size,
      skills,
      page = 1,
      limit = 20,
    } = req.query;

    const offset = (Number(page) - 1) * Number(limit);

    let whereConditions: string[] = [
      "is_active = true",
      "account_status = 'active'",
      "is_public = true",
    ];
    const values: any[] = [];
    let paramCounter = 1;

    // Account type filter
    if (
      account_type &&
      (account_type === "user" || account_type === "company")
    ) {
      whereConditions.push(`account_type = $${paramCounter}`);
      values.push(account_type);
      paramCounter++;
    }

    // Location filters
    if (location_country) {
      whereConditions.push(`location_country ILIKE $${paramCounter}`);
      values.push(`%${location_country}%`);
      paramCounter++;
    }

    if (location_city) {
      whereConditions.push(`location_city ILIKE $${paramCounter}`);
      values.push(`%${location_city}%`);
      paramCounter++;
    }

    // Company size filter
    if (account_type === "company" && company_size) {
      whereConditions.push(`company_size = $${paramCounter}`);
      values.push(company_size);
      paramCounter++;
    }

    // Skills filter (for users)
    if (account_type === "user" && skills) {
      const skillsArray = Array.isArray(skills) ? skills : [skills];
      whereConditions.push(`user_skills @> $${paramCounter}`);
      values.push(skillsArray);
      paramCounter++;
    }

    // Search query (full-text search)
    let searchQuery = "";
    if (query) {
      searchQuery = `
      AND (
        to_tsvector('english', COALESCE(display_name, '')) @@ to_tsquery('english', $${paramCounter})
        OR to_tsvector('english', COALESCE(bio, '')) @@ to_tsquery('english', $${paramCounter})
        OR to_tsvector('english', COALESCE(user_personal_bio, '')) @@ to_tsquery('english', $${paramCounter})
        OR to_tsvector('english', COALESCE(company_description, '')) @@ to_tsquery('english', $${paramCounter})
        OR username ILIKE $${paramCounter + 1}
        OR display_name ILIKE $${paramCounter + 1}
      )
    `;
      values.push((query as string).replace(/\s+/g, " & "));
      values.push(`%${query}%`);
      paramCounter += 2;
    }

    // Build the query
    const baseQuery = `
    SELECT 
      id,
      account_type,
      username,
      display_name,
      avatar,
      bio,
      location_city,
      location_country,
      profile_completion_percentage,
      created_at,
      user_full_name,
      user_job_title,
      user_current_company,
      user_skills,
      company_name,
      company_size,
      company_description
    FROM accounts
    WHERE ${whereConditions.join(" AND ")} ${searchQuery}
    ORDER BY profile_completion_percentage DESC, created_at DESC
  `;

    // Count query
    const countQuery = `
    SELECT COUNT(*) as total
    FROM accounts
    WHERE ${whereConditions.join(" AND ")} ${searchQuery}
  `;

    // Paginated query
    const paginatedQuery = `
    ${baseQuery}
    LIMIT $${paramCounter}
    OFFSET $${paramCounter + 1}
  `;

    values.push(Number(limit));
    values.push(offset);

    // Execute queries
    const [countResult, accountsResult] = await Promise.all([
      database.query(countQuery, values.slice(0, -2)),
      database.query(paginatedQuery, values),
    ]);

    const total = parseInt(countResult.rows[0]?.total || 0);
    const accounts = accountsResult.rows;

    res.status(200).json({
      success: true,
      data: {
        accounts,
        pagination: {
          page: Number(page),
          limit: Number(limit),
          total,
          pages: Math.ceil(total / Number(limit)),
        },
      },
    });
  },
);

// controller: get account statistics
export const getAccountStats = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    const account = (req as any).user;

    // Get account statistics
    const statsResult = await database.query(
      `SELECT 
      login_count,
      profile_completion_percentage,
      created_at,
      last_login_at,
      email_verified_at
    FROM accounts WHERE id = $1`,
      [account.id],
    );

    const stats = statsResult.rows[0];

    // Get additional stats based on account type
    let additionalStats = {};

    if (account.account_type === "user") {
      // You can add user-specific stats here (e.g., connections, projects, etc.)
      additionalStats = {
        skills_count: account.user_skills ? account.user_skills.length : 0,
      };
    } else if (account.account_type === "company") {
      // You can add company-specific stats here (e.g., employees, products, etc.)
      additionalStats = {
        company_size: account.company_size,
        founded_year: account.company_founded_year,
      };
    }

    res.status(200).json({
      success: true,
      data: {
        stats: {
          ...stats,
          ...additionalStats,
        },
      },
    });
  },
);

export const getAllAccounts = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    // This is a placeholder for the admin route to get all accounts
    res.status(200).json({
      success: true,
      data: {
        accounts: [], // You would implement the actual logic to fetch accounts here
      },
    });
  },
);

export const updateAccountStatus = catchAsyncError(
  async (req: Request, res: Response, next: NextFunction) => {
    // This is a placeholder for the admin route to update account status
    res.status(200).json({
      success: true,
      message: "Account status updated successfully.",
    });
  },
);
