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

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

  async create({ email, password, firstName, lastName, phone, referralEmail }) {
    logger.info('PersonalUserRepository.create:start', { email, firstName, lastName });
    try {
      console.log('📊 PersonalUserRepository: Creating personal user in database...');
      const hashedPassword = await User.hashPassword(password);

      const conn = this.unitOfWork.connection;
      // 1. Insert into users table
      const userQuery = `
        INSERT INTO users (email, password, user_type, role)
        VALUES (?, ?, 'personal', 'user')
      `;
      const [userResult] = await conn.query(userQuery, [email, hashedPassword]);
      const userId = userResult.insertId;

      logger.info('PersonalUserRepository.create:user_created', { userId });
      console.log('✅ User record created with ID:', userId);

      // 2. Insert into personal_profiles table
      const profileQuery = `
        INSERT INTO personal_profiles (user_id, first_name, last_name, phone)
        VALUES (?, ?, ?, ?)
      `;
      await conn.query(profileQuery, [userId, firstName, lastName, phone]);

      logger.info('PersonalUserRepository.create:profile_created', { userId });
      console.log('✅ Personal profile created');

      logger.info('PersonalUserRepository.create:success', { userId });
      return new PersonalUser(
        userId,
        email,
        hashedPassword,
        firstName,
        lastName,
        phone,
        null,
        referralEmail,
        new Date()
      );
    } catch (error) {
      logger.error('PersonalUserRepository.create:error', { email, error: error.message });
      throw error;
    }
  }

  async findByEmail(email) {
    logger.info('PersonalUserRepository.findByEmail:start', { email });
    try {
      console.log('🔍 PersonalUserRepository: Querying database for personal user...');
      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('PersonalUserRepository.findByEmail: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('PersonalUserRepository.findByEmail:not_found', { email });
      return null;
    } catch (error) {
      logger.error('PersonalUserRepository.findByEmail:error', { email, error: error.message });
      throw error;
    }
  }

  async updateProfileAndMarkCompleted(userId, profileData) {
    logger.info('PersonalUserRepository.updateProfileAndMarkCompleted:start', { userId });
    try {
      const conn = this.unitOfWork.connection;
      const {
        dateOfBirth,
        nationality,
        address,
        zip_code,
        city, // Added city
        country,
        phone,
        phoneSecondary,
        emergencyContactName,
        emergencyContactPhone,
        accountHolderName,
        iban // Added field
      } = profileData;

      const firstName =
        profileData.firstName ||
        profileData.first_name ||
        '';
      const lastName =
        profileData.lastName ||
        profileData.last_name ||
        '';

      await conn.query(
        `INSERT INTO personal_profiles (
          user_id, first_name, last_name, phone, date_of_birth, nationality, address, zip_code, city, country,
          phone_secondary, emergency_contact_name, emergency_contact_phone, account_holder_name
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ON DUPLICATE KEY UPDATE
          first_name = VALUES(first_name),
          last_name = VALUES(last_name),
          phone = VALUES(phone),
          date_of_birth = VALUES(date_of_birth),
          nationality = VALUES(nationality),
          address = VALUES(address),
          zip_code = VALUES(zip_code),
          city = VALUES(city),
          country = VALUES(country),
          phone_secondary = VALUES(phone_secondary),
          emergency_contact_name = VALUES(emergency_contact_name),
          emergency_contact_phone = VALUES(emergency_contact_phone),
          account_holder_name = VALUES(account_holder_name)`,
        [
          userId,
          firstName,
          lastName,
          phone || null,
          dateOfBirth,
          nationality,
          address,
          zip_code,
          city, // Added city
          country,
          phoneSecondary,
          emergencyContactName,
          emergencyContactPhone,
          accountHolderName
        ]
      );

      logger.info('PersonalUserRepository.updateProfileAndMarkCompleted:profile_updated', { userId });

      // Update IBAN in users table if provided
      if (iban) {
        await conn.query(
          `UPDATE users SET iban = ? WHERE id = ?`,
          [iban, userId]
        );
        logger.info('PersonalUserRepository.updateProfileAndMarkCompleted:iban_updated', { userId });
      }

      await conn.query(
        `UPDATE user_status SET profile_completed = 1, profile_completed_at = NOW() WHERE user_id = ?`,
        [userId]
      );
      logger.info('PersonalUserRepository.updateProfileAndMarkCompleted:profile_completed', { userId });
    } catch (error) {
      logger.error('PersonalUserRepository.updateProfileAndMarkCompleted:error', { userId, error: error.message });
      throw error;
    }
  }

  async findById(userId) {
    logger.info('PersonalUserRepository.findById:start', { userId });
    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.id = ? AND u.user_type = 'personal'
      `;
      const [rows] = await conn.query(query, [userId]);
      if (rows.length > 0) {
        logger.info('PersonalUserRepository.findById:found', { userId });
        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('PersonalUserRepository.findById:not_found', { userId });
      return null;
    } catch (error) {
      logger.error('PersonalUserRepository.findById:error', { userId, error: error.message });
      throw error;
    }
  }

  // Returns merged profile shape used by "GET /api/me"-like responses
  async getMergedProfileById(userId) {
    const conn = this.unitOfWork.connection;
    const [rows] = await conn.query(
      `
      SELECT 
        u.id, u.email, u.user_type, u.role, u.iban, u.created_at, u.updated_at,
        pp.first_name, pp.last_name, pp.phone, pp.address, pp.date_of_birth, pp.account_holder_name
      FROM users u
      LEFT JOIN personal_profiles pp ON u.id = pp.user_id
      WHERE u.id = ?
      `,
      [userId]
    );
    if (!rows.length) return null;
    const r = rows[0];
    return {
      id: r.id,
      email: r.email,
      user_type: r.user_type,
      role: r.role,
      firstName: r.first_name,
      lastName: r.last_name,
      phone: r.phone,
      address: r.address,
      dateOfBirth: r.date_of_birth,
      accountHolderName: r.account_holder_name,
      iban: r.iban,
      createdAt: r.created_at,
      updatedAt: r.updated_at
    };
  }

  async updateBasicInfo(userId, payload) {
    logger.info('PersonalUserRepository.updateBasicInfo:start', { userId });
    const conn = this.unitOfWork.connection;

    // Lock current rows to compute diffs safely
    const [[currentUser]] = await conn.query(
      `SELECT id, email FROM users WHERE id = ? FOR UPDATE`,
      [userId]
    );
    const [[currentProfile]] = await conn.query(
      `SELECT first_name, last_name, phone, address FROM personal_profiles WHERE user_id = ? FOR UPDATE`,
      [userId]
    );
    if (!currentUser) {
      logger.warn('PersonalUserRepository.updateBasicInfo:not_found', { userId });
      return null;
    }

    const { firstName, lastName, email, phone, address } = payload;

    // Compute diffs
    const changes = {};
    const setProfile = {};
    const setUser = {};

    if (email !== undefined && email !== currentUser.email) {
      // Uniqueness check
      const [conflict] = await conn.query(
        `SELECT id FROM users WHERE email = ? AND id <> ? LIMIT 1`,
        [email, userId]
      );
      if (conflict.length) {
        const err = new Error('Email already in use');
        err.code = 'EMAIL_CONFLICT';
        throw err;
      }
      setUser.email = email;
      changes.email = { from: currentUser.email, to: email };
    }

    if (firstName !== undefined && firstName !== currentProfile?.first_name) {
      setProfile.first_name = firstName;
      changes.firstName = { from: currentProfile?.first_name ?? null, to: firstName };
    }
    if (lastName !== undefined && lastName !== currentProfile?.last_name) {
      setProfile.last_name = lastName;
      changes.lastName = { from: currentProfile?.last_name ?? null, to: lastName };
    }
    if (phone !== undefined && phone !== currentProfile?.phone) {
      setProfile.phone = phone;
      changes.phone = { from: currentProfile?.phone ?? null, to: phone };
    }
    if (address !== undefined && address !== currentProfile?.address) {
      setProfile.address = address;
      changes.address = { from: currentProfile?.address ?? null, to: address };
    }

    // Persist updates
    if (Object.keys(setUser).length) {
      await conn.query(
        `UPDATE users SET ${Object.keys(setUser).map(k => `${k} = ?`).join(', ')} WHERE id = ?`,
        [...Object.values(setUser), userId]
      );
      // Email changed: reset verification flags
      if (setUser.email) {
        await conn.query(
          `UPDATE user_status SET email_verified = 0, email_verified_at = NULL WHERE user_id = ?`,
          [userId]
        );
      }
    }
    if (Object.keys(setProfile).length) {
      await conn.query(
        `UPDATE personal_profiles SET ${Object.keys(setProfile).map(k => `${k} = ?`).join(', ')} WHERE user_id = ?`,
        [...Object.values(setProfile), userId]
      );
    }

    // Audit log
    if (Object.keys(changes).length) {
      await conn.query(
        `INSERT INTO user_action_logs (user_id, action, details) VALUES (?, 'profile_update_basic', ?)`,
        [userId, JSON.stringify({ changes })]
      );
    }

    logger.info('PersonalUserRepository.updateBasicInfo:success', { userId, changedKeys: Object.keys(changes) });
    return this.getMergedProfileById(userId);
  }

  async updateBankInfo(userId, payload) {
    logger.info('PersonalUserRepository.updateBankInfo:start', { userId, payloadPreview: { ...payload, ...(payload?.iban ? { iban: `****${String(payload.iban).slice(-4)}` } : {}) } });
    const conn = this.unitOfWork.connection;

    // Lock current rows to compute diffs safely
    const [[currentUser]] = await conn.query(
      `SELECT id, iban FROM users WHERE id = ? FOR UPDATE`,
      [userId]
    );
    const [[currentProfile]] = await conn.query(
      `SELECT account_holder_name FROM personal_profiles WHERE user_id = ? FOR UPDATE`,
      [userId]
    );
    if (!currentUser) {
      logger.warn('PersonalUserRepository.updateBankInfo:not_found', { userId });
      return null;
    }

    const { accountHolderName, iban } = payload;

    const changes = {};
    const setUser = {};
    const setProfile = {};

    if (iban !== undefined && iban !== currentUser.iban) {
      setUser.iban = iban;
      changes.iban = { from: currentUser.iban ? `****${String(currentUser.iban).slice(-4)}` : null, to: `****${String(iban).slice(-4)}` };
    }
    if (accountHolderName !== undefined && accountHolderName !== currentProfile?.account_holder_name) {
      setProfile.account_holder_name = accountHolderName;
      changes.accountHolderName = {
        from: currentProfile?.account_holder_name ?? null,
        to: accountHolderName
      };
    }

    logger.info('PersonalUserRepository.updateBankInfo:diff', {
      userId,
      willUpdateUsers: Object.keys(setUser),
      willUpdateProfile: Object.keys(setProfile),
      changes
    });

    if (Object.keys(setUser).length) {
      await conn.query(
        `UPDATE users SET ${Object.keys(setUser).map(k => `${k} = ?`).join(', ')} WHERE id = ?`,
        [...Object.values(setUser), userId]
      );
    }
    if (Object.keys(setProfile).length) {
      await conn.query(
        `UPDATE personal_profiles SET ${Object.keys(setProfile).map(k => `${k} = ?`).join(', ')} WHERE user_id = ?`,
        [...Object.values(setProfile), userId]
      );
    }

    if (Object.keys(changes).length) {
      await conn.query(
        `INSERT INTO user_action_logs (user_id, action, details) VALUES (?, 'profile_update_bank', ?)`,
        [userId, JSON.stringify({ changes })]
      );
    }

    logger.info('PersonalUserRepository.updateBankInfo:success', { userId, changedKeys: Object.keys(changes) });
    return this.getMergedProfileById(userId);
  }
}

module.exports = PersonalUserRepository;