const PersonalUser = require('../../models/PersonalUser');
const CompanyUser = require('../../models/CompanyUser');
const User = require('../../models/User');
const { logger } = require('../../middleware/logger');

class UserRepository {
  constructor(unitOfWork) {
    this.unitOfWork = unitOfWork;
  }

  async createUser(email, hashedPassword, userType) {
    logger.info('UserRepository.createUser:start', { email, userType });
    try {
      const conn = this.unitOfWork.connection;
      const query = `
        INSERT INTO users (email, password, user_type, role)
        VALUES (?, ?, ?, 'user')
      `;
      const [result] = await conn.query(query, [email, hashedPassword, userType]);
      logger.info('UserRepository.createUser:success', { email, userType, userId: result.insertId });
      return result.insertId;
    } catch (error) {
      logger.error('UserRepository.createUser:error', { email, userType, error: error.message });
      throw error;
    }
  }

  async findUserByEmail(email) {
    logger.info('UserRepository.findUserByEmail:start', { email });
    try {
      const conn = this.unitOfWork.connection;
      // Query user, company profile, and personal profile data
      const query = `
        SELECT u.*, 
               cp.company_name, cp.registration_number, cp.phone as company_phone, 
               cp.contact_person_name, cp.contact_person_phone,
               pp.first_name, pp.last_name, pp.phone as personal_phone, pp.date_of_birth
        FROM users u
        LEFT JOIN company_profiles cp ON u.id = cp.user_id
        LEFT JOIN personal_profiles pp ON u.id = pp.user_id
        WHERE u.email = ?
      `;
      const [rows] = await conn.query(query, [email]);
      if (rows.length > 0) {
        logger.info('UserRepository.findUserByEmail:found', { email, userId: rows[0].id });
        const row = rows[0];
        if (row.user_type === 'personal') {
          return new PersonalUser(
            row.id, row.email, row.password, row.first_name || '', row.last_name || '',
            row.phone || '', row.date_of_birth || null, null, row.created_at, row.updated_at, row.role
          );
        } else if (row.user_type === 'company') {
          return new CompanyUser(
            row.id, row.email, row.password, row.company_name || '', row.company_phone || '',
            row.contact_person_name || '', row.contact_person_phone || '', row.registration_number || '',
            row.created_at, row.updated_at, row.role
          );
        } else {
          return new User(
            row.id, row.email, row.password, row.user_type, row.created_at, row.updated_at, row.role
          );
        }
      }
      logger.info('UserRepository.findUserByEmail:not_found', { email });
      return null;
    } catch (error) {
      logger.error('UserRepository.findUserByEmail:error', { email, error: error.message });
      throw error;
    }
  }

  async createPersonalProfile(userId, profileData) {
    logger.info('UserRepository.createPersonalProfile:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      const { firstName, lastName, phone } = profileData;
      const query = `
        INSERT INTO personal_profiles (user_id, first_name, last_name, phone)
        VALUES (?, ?, ?, ?)
      `;
      await conn.query(query, [userId, firstName, lastName, phone]);
      logger.info('UserRepository.createPersonalProfile:success', { userId });
    } catch (error) {
      logger.error('UserRepository.createPersonalProfile:error', { userId, error: error.message });
      throw error;
    }
  }

  async findPersonalUserByEmail(email) {
    logger.info('UserRepository.findPersonalUserByEmail:start', { email });
    try {
      const conn = this.unitOfWork.connection;
      const query = `
        SELECT u.*, pp.first_name, pp.last_name, pp.phone, pp.date_of_birth
        FROM users u
        LEFT JOIN personal_profiles pp ON u.id = pp.user_id
        WHERE u.email = ? AND u.user_type = 'personal'
      `;
      const [rows] = await conn.query(query, [email]);
      if (rows.length > 0) {
        logger.info('UserRepository.findPersonalUserByEmail:found', { email, userId: rows[0].id });
        const row = rows[0];
        return new PersonalUser(
          row.id, row.email, row.password, row.first_name,
          row.last_name, row.phone, row.date_of_birth, null,
          row.created_at, row.updated_at
        );
      }
      logger.info('UserRepository.findPersonalUserByEmail:not_found', { email });
      return null;
    } catch (error) {
      logger.error('UserRepository.findPersonalUserByEmail:error', { email, error: error.message });
      throw error;
    }
  }

  async createCompanyProfile(userId, profileData) {
    logger.info('UserRepository.createCompanyProfile:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      const { companyName, registrationNumber, companyPhone, contactPersonName } = profileData;
      const query = `
        INSERT INTO company_profiles (user_id, company_name, registration_number, phone, contact_person_name)
        VALUES (?, ?, ?, ?, ?)
      `;
      await conn.query(query, [userId, companyName, registrationNumber, companyPhone, contactPersonName]);
      logger.info('UserRepository.createCompanyProfile:success', { userId });
    } catch (error) {
      logger.error('UserRepository.createCompanyProfile:error', { userId, error: error.message });
      throw error;
    }
  }

  async findCompanyUserByEmail(email) {
    logger.info('UserRepository.findCompanyUserByEmail:start', { email });
    try {
      const conn = this.unitOfWork.connection;
      const query = `
        SELECT u.*, cp.company_name, cp.registration_number, cp.phone as company_phone, cp.contact_person_name
        FROM users u
        LEFT JOIN company_profiles cp ON u.id = cp.user_id
        WHERE u.email = ? AND u.user_type = 'company'
      `;
      const [rows] = await conn.query(query, [email]);
      if (rows.length > 0) {
        logger.info('UserRepository.findCompanyUserByEmail:found', { email, userId: rows[0].id });
        const row = rows[0];
        return new CompanyUser(
          row.id, row.email, row.password, row.company_name,
          row.company_phone, row.contact_person_name, null,
          row.registration_number, row.created_at, row.updated_at
        );
      }
      logger.info('UserRepository.findCompanyUserByEmail:not_found', { email });
      return null;
    } catch (error) {
      logger.error('UserRepository.findCompanyUserByEmail:error', { email, error: error.message });
      throw error;
    }
  }

  async createUserStatus(userId) {
    logger.info('UserRepository.createUserStatus:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      const query = `
        INSERT INTO user_status (user_id, status, email_verified, profile_completed, registration_completed)
        VALUES (?, 'pending', FALSE, TRUE, FALSE)
      `;
      await conn.query(query, [userId]);
      logger.info('UserRepository.createUserStatus:success', { userId });
    } catch (error) {
      logger.error('UserRepository.createUserStatus:error', { userId, error: error.message });
      throw error;
    }
  }

  async findUserByEmailOrId(identifier) {
    logger.info('UserRepository.findUserByEmailOrId:start', { identifier });
    try {
      const conn = this.unitOfWork.connection;
      let query, params;
      if (typeof identifier === 'number' || /^\d+$/.test(identifier)) {
        query = `
          SELECT u.*, 
                 cp.company_name, cp.registration_number, cp.phone as company_phone, 
                 cp.contact_person_name, cp.contact_person_phone,
                 pp.first_name, pp.last_name, pp.phone as personal_phone, pp.date_of_birth
          FROM users u
          LEFT JOIN company_profiles cp ON u.id = cp.user_id
          LEFT JOIN personal_profiles pp ON u.id = pp.user_id
          WHERE u.id = ?
        `;
        params = [identifier];
      } else {
        query = `
          SELECT u.*, 
                 cp.company_name, cp.registration_number, cp.phone as company_phone, 
                 cp.contact_person_name, cp.contact_person_phone,
                 pp.first_name, pp.last_name, pp.phone as personal_phone, pp.date_of_birth
          FROM users u
          LEFT JOIN company_profiles cp ON u.id = cp.user_id
          LEFT JOIN personal_profiles pp ON u.id = pp.user_id
          WHERE u.email = ?
        `;
        params = [identifier];
      }
      const [rows] = await conn.query(query, params);
      if (rows.length > 0) {
        logger.info('UserRepository.findUserByEmailOrId:found', { identifier, userId: rows[0].id });
        const row = rows[0];
        if (row.user_type === 'company') {
          return new CompanyUser(
            row.id, row.email, row.password, row.company_name || '', row.company_phone || '',
            row.contact_person_name || '', row.contact_person_phone || '', row.registration_number || '',
            row.created_at, row.updated_at
          );
        } else if (row.user_type === 'personal') {
          return new PersonalUser(
            row.id, row.email, row.password, row.first_name || '', row.last_name || '',
            row.personal_phone || '', row.date_of_birth || null, null,
            row.created_at, row.updated_at
          );
        } else {
          return new User(
            row.id, row.email, row.password, row.user_type, row.created_at, row.updated_at
          );
        }
      }
      logger.info('UserRepository.findUserByEmailOrId:not_found', { identifier });
      return null;
    } catch (error) {
      logger.error('UserRepository.findUserByEmailOrId:error', { identifier, error: error.message });
      throw error;
    }
  }

  async getIban(userId) {
    logger.info('UserRepository.getIban:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      const [rows] = await conn.query(
        `SELECT iban FROM users WHERE id = ? LIMIT 1`, [userId]
      );
      logger.info('UserRepository.getIban:success', { userId, iban: rows.length ? rows[0].iban : null });
      return rows.length ? rows[0].iban : null;
    } catch (error) {
      logger.error('UserRepository.getIban:error', { userId, error: error.message });
      throw error;
    }
  }

  async getProfile(userId, userType) {
    logger.info('UserRepository.getProfile:start', { userId, userType });
    try {
      const conn = this.unitOfWork.connection;
      if (userType === 'personal') {
        const [rows] = await conn.query(
          `SELECT * FROM personal_profiles WHERE user_id = ? LIMIT 1`, [userId]
        );
        logger.info('UserRepository.getProfile:success', { userId, userType });
        return rows.length ? rows[0] : null;
      } else if (userType === 'company') {
        const [rows] = await conn.query(
          `SELECT * FROM company_profiles WHERE user_id = ? LIMIT 1`, [userId]
        );
        logger.info('UserRepository.getProfile:success', { userId, userType });
        return rows.length ? rows[0] : null;
      }
      logger.info('UserRepository.getProfile:not_found', { userId, userType });
      return null;
    } catch (error) {
      logger.error('UserRepository.getProfile:error', { userId, userType, error: error.message });
      throw error;
    }
  }

  async getReferralEmail(userId, userType) {
    logger.info('UserRepository.getReferralEmail:start', { userId, userType });
    try {
      const conn = this.unitOfWork.connection;
      const [rows] = await conn.query(
        `SELECT u.email AS referralEmail
         FROM referral_token_usage rtu
         JOIN referral_tokens rt ON rtu.referral_token_id = rt.id
         JOIN users u ON rt.created_by_user_id = u.id
         WHERE rtu.used_by_user_id = ? LIMIT 1`,
        [userId]
      );
      logger.info('UserRepository.getReferralEmail:success', { userId, referralEmail: rows.length ? rows[0].referralEmail : null });
      return rows.length ? rows[0].referralEmail : null;
    } catch (error) {
      logger.error('UserRepository.getReferralEmail:error', { userId, userType, error: error.message });
      throw error;
    }
  }

  async getPermissions(userId) {
    logger.info('UserRepository.getPermissions:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      const [permRows] = await conn.query(
        `SELECT p.name FROM user_permissions up
         JOIN permissions p ON up.permission_id = p.id
         WHERE up.user_id = ? AND p.is_active = TRUE`,
        [userId]
      );
      logger.info('UserRepository.getPermissions:success', { userId, count: permRows.length });
      return permRows.map(row => row.name);
    } catch (error) {
      logger.error('UserRepository.getPermissions:error', { userId, error: error.message });
      throw error;
    }
  }

  async getUserStatus(userId) {
    logger.info('UserRepository.getUserStatus:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      const [rows] = await conn.query(
        `SELECT * FROM user_status WHERE user_id = ? LIMIT 1`, [userId]
      );
      logger.info('UserRepository.getUserStatus:success', { userId, found: rows.length > 0 });
      return rows.length ? rows[0] : null;
    } catch (error) {
      logger.error('UserRepository.getUserStatus:error', { userId, error: error.message });
      throw error;
    }
  }

  async getContracts(userId) {
    logger.info('UserRepository.getContracts:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      const [rows] = await conn.query(
        `SELECT * FROM user_documents WHERE user_id = ? AND document_type = 'contract'`, [userId]
      );
      logger.info('UserRepository.getContracts:success', { userId, count: rows.length });
      return rows;
    } catch (error) {
      logger.error('UserRepository.getContracts:error', { userId, error: error.message });
      throw error;
    }
  }

  async getIdDocuments(userId) {
    logger.info('UserRepository.getIdDocuments:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      const [rows] = await conn.query(
        `SELECT * FROM user_id_documents WHERE user_id = ?`, [userId]
      );
      logger.info('UserRepository.getIdDocuments:success', { userId, count: rows.length });
      return rows;
    } catch (error) {
      logger.error('UserRepository.getIdDocuments:error', { userId, error: error.message });
      throw error;
    }
  }

  async deleteUserById(userId) {
    logger.info('UserRepository.deleteUserById:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      await conn.query(`DELETE FROM users WHERE id = ?`, [userId]);
      logger.info('UserRepository.deleteUserById:success', { userId });
    } catch (error) {
      logger.error('UserRepository.deleteUserById:error', { userId, error: error.message });
      throw error;
    }
  }

  async getStatusByUserId(userId) {
    logger.info('UserRepository.getStatusByUserId:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      const [rows] = await conn.query(
        `SELECT 
          email_verified, 
          documents_uploaded, 
          profile_completed, 
          contract_signed, 
          is_admin_verified,
          email_verified_at,
          documents_uploaded_at,
          profile_completed_at,
          contract_signed_at
         FROM user_status WHERE user_id = ? LIMIT 1`, [userId]
      );
      if (rows.length) {
        const row = rows[0];
        logger.info('UserRepository.getStatusByUserId:success', { userId, status: row });
        return {
          email_verified: row.email_verified,
          documents_uploaded: row.documents_uploaded,
          profile_completed: row.profile_completed,
          contract_signed: row.contract_signed,
          is_admin_verified: row.is_admin_verified,
          email_verified_at: row.email_verified_at,
          documents_uploaded_at: row.documents_uploaded_at,
          profile_completed_at: row.profile_completed_at,
          contract_signed_at: row.contract_signed_at
        };
      }
      logger.info('UserRepository.getStatusByUserId:not_found', { userId });
      return null;
    } catch (error) {
      logger.error('UserRepository.getStatusByUserId:error', { userId, error: error.message });
      throw error;
    }
  }
}

module.exports = UserRepository;
