const bcrypt = require('bcrypt');
const logger = require('../utils/logger');
const BaseController = require('./base.controller');
const {
  Cliente,
  Cita,
  Antecedenteheredofamiliarhistoria,
  Antecedentenopatologicohistoria,
  Antecedentepatologicohistoria
} = require('../models');

class ClienteController extends BaseController {
  constructor() {
    super(Cliente, 'Cliente', ['Name', 'LastName', 'Email', 'DocumentNumber', 'Phone']);
  }

  // Sobrescribir getById para excluir password e incluir relaciones
  getById = async (req, res, next) => {
    try {
      const {
        id
      } = req.params;
      const data = await this.model.findByPk(id, {
        attributes: {
          exclude: ['Password']
        },
        include: [{
          model: Cita,
          as: 'citas',
          limit: 5,
          order: [
            ['Appointment', 'DESC']
          ]
        }]
      });

      if (!data) {
        return res.status(404).json({
          success: false,
          message: `${this.modelName} no encontrado`
        });
      }

      res.json({
        success: true,
        message: `${this.modelName} obtenido exitosamente`,
        data
      });
    } catch (error) {
      next(error);
    }
  };

  // Sobrescribir getAll para excluir password
  getAll = async (req, res, next) => {
    try {
      const {
        search, sortBy, sortOrder, ...filters
      } = req.query;

      const {
        buildQueryOptions
      } = require('../utils/queryBuilder');

      const queryOptions = buildQueryOptions({
        search,
        searchFields: this.searchFields,
        sortBy,
        sortOrder,
        ...filters
      });

      queryOptions.attributes = {
        exclude: ['Password']
      };

      const data = await this.model.findAndCountAll(queryOptions);

      res.json({
        success: true,
        message: `${this.modelName} obtenidos exitosamente`,
        ...data
      });
    } catch (error) {
      next(error);
    }
  };

  // Buscar por documento
  findByDocument = async (req, res, next) => {
    try {
      const {
        document
      } = req.params;
      const cliente = await Cliente.findOne({
        where: {
          DocumentNumber: document
        },
        attributes: {
          exclude: ['Password']
        }
      });

      if (!cliente) {
        return res.status(404).json({
          success: false,
          message: 'Cliente no encontrado'
        });
      }

      res.json({
        success: true,
        data: cliente
      });
    } catch (error) {
      next(error);
    }
  };

  // Búsqueda flexible por nombre y/o apellido (un solo parámetro)
  findByName = async (req, res, next) => {
    try {
      const { query } = req.params;
      if (!query || query.trim() === '') {
        return res.status(400).json({
          success: false,
          message: 'Parámetro de búsqueda requerido'
        });
      }
      const termino = query.trim();
      const palabras = termino.split(/\s+/).filter(p => p); // Divide en palabras
      const { Op } = require('sequelize');
      let where = {};
      if (palabras.length === 1) {
        // Una sola palabra → busca en nombre o apellido
        where = {
          [Op.or]: [
            { Name: { [Op.like]: `%${palabras[0]}%` } },
            { LastName: { [Op.like]: `%${palabras[0]}%` } }
          ]
        };
      } else {
        // Dos o más palabras → busca en ambos órdenes + coincidencias parciales
        where = {
          [Op.or]: [
            // Orden normal: Nombre + Apellido(s)
            {
              [Op.and]: palabras.map((palabra, i) =>
                i === 0
                  ? { Name: { [Op.like]: `%${palabra}%` } }
                  : { LastName: { [Op.like]: `%${palabra}%` } }
              )
            },
            // Orden invertido: Apellido + Nombre
            {
              [Op.and]: palabras.slice().reverse().map((palabra, i) =>
                i === 0
                  ? { Name: { [Op.like]: `%${palabra}%` } }
                  : { LastName: { [Op.like]: `%${palabra}%` } }
              )
            },
            // Bonus: cualquier palabra coincida en nombre o apellido
            ...palabras.map(palabra => ({
              [Op.or]: [
                { Name: { [Op.like]: `%${palabra}%` } },
                { LastName: { [Op.like]: `%${palabra}%` } }
              ]
            }))
          ]
        };
      }
      const clientes = await Cliente.findAll({
        where,
        attributes: { exclude: ['Password'] },
        limit: 20,
        order: [['LastName', 'ASC'], ['Name', 'ASC']]
      });
      if (clientes.length === 0) {
        return res.status(404).json({
          success: false,
          message: 'No se encontraron clientes'
        });
      }
      res.json({
        success: true,
        count: clientes.length,
        data: clientes
      });
    } catch (error) {
      next(error);
    }
  };

  // Obtener historial médico completo
  getHistorialMedico = async (req, res, next) => {
    try {
      const {
        id
      } = req.params;

      const cliente = await Cliente.findByPk(id, {
        attributes: {
          exclude: ['Password']
        },
        include: [{
            model: Antecedenteheredofamiliarhistoria,
            as: 'antecedentesheredofamiliares'
          },
          {
            model: Antecedentenopatologicohistoria,
            as: 'antecedentesnopatologicos'
          },
          {
            model: Antecedentepatologicohistoria,
            as: 'antecedentespatologicos'
          },
          {
            model: Cita,
            as: 'citas',
            order: [
              ['Appointment', 'DESC']
            ]
          }
        ]
      });

      if (!cliente) {
        return res.status(404).json({
          success: false,
          message: 'Cliente no encontrado'
        });
      }

      res.json({
        success: true,
        message: 'Historial médico obtenido exitosamente',
        data: cliente
      });
    } catch (error) {
      next(error);
    }
  };

  /**
   * Resetear contraseña de Cliente usando DocumentNumber
   */
  resetPasswordByDocumentNumber = async (req, res) => {
    try {
      const {
        clienteId,
        newPassword
      } = req.body;

      // Validar campos requeridos
      if (!clienteId || !newPassword) {
        return res.status(400).json({
          success: false,
          message: 'ID de cliente y nueva contraseña son requeridos'
        });
      }

      // Validar longitud de contraseña
      if (newPassword.length < 2) {
        return res.status(400).json({
          success: false,
          message: 'La contraseña debe tener al menos 2 caracteres'
        });
      }

      // Buscar cliente por número de documento
      const cliente = await Cliente.findOne({
        where: {
          id: clienteId
        }
      });

      if (!cliente) {
        logger.warn('Reset password fallido - Cliente no encontrado', {
          documentNumber,
          ip: req.ip
        });
        return res.status(404).json({
          success: false,
          message: 'No se encontró un paciente con ese número de documento'
        });
      }

      // Hashear nueva contraseña
      const hashedPassword = await bcrypt.hash(
        newPassword,
        parseInt(process.env.BCRYPT_ROUNDS) || 10
      );

      // Actualizar contraseña
      await cliente.update({
        Password: hashedPassword
      });

      // Log exitoso
      logger.auth('Contraseña reseteada exitosamente - Cliente', {
        clienteId: cliente.Id,
        documentNumber: cliente.DocumentNumber,
        ip: req.ip
      });

      res.json({
        success: true,
        message: 'Contraseña reseteada exitosamente',
        data: {
          email: cliente.Email,
          name: cliente.Name
        }
      });

    } catch (error) {
      logger.error('Error al resetear contraseña de cliente', {
        error: error.message,
        stack: error.stack
      });
      res.status(500).json({
        success: false,
        message: 'Error en el servidor',
        error: process.env.NODE_ENV === 'development' ? error.message : undefined
      });
    }
  };

  /**
   * Verificar si existe un cliente por número de documento
   */
  verifyDocumentNumberExists = async (req, res) => {
    try {
      const {
        documentNumber
      } = req.params;

      const cliente = await Cliente.findOne({
        where: {
          DocumentNumber: documentNumber
        },
        attributes: ['Id', 'Name', 'Email', 'DocumentType']
      });

      if (!cliente) {
        return res.status(404).json({
          success: false,
          message: 'No se encontró un paciente con ese número de documento'
        });
      }

      res.json({
        success: true,
        message: 'Paciente encontrado',
        data: {
          name: cliente.Name,
          email: cliente.Email.replace(/(.{2})(.*)(@.*)/, '\$1***\$3'), // Ocultar parte del email
          documentType: cliente.DocumentType
        }
      });

    } catch (error) {
      logger.error('Error al verificar número de documento', {
        error: error.message
      });
      res.status(500).json({
        success: false,
        message: 'Error en el servidor'
      });
    }
  };

  /**
   * Solicitar código de verificación para cliente
   */
  requestVerificationCode = async (req, res) => {
    try {
      const {
        documentNumber
      } = req.body;

      const cliente = await Cliente.findOne({
        where: {
          DocumentNumber: documentNumber
        }
      });

      if (!cliente) {
        return res.status(404).json({
          success: false,
          message: 'No se encontró un paciente con ese número de documento'
        });
      }

      // Generar código de 6 dígitos
      const verificationCode = Math.floor(100000 + Math.random() * 900000).toString();

      // TODO: Aquí deberías enviar el código por email o SMS

      logger.auth('Código de verificación generado - Cliente', {
        clienteId: cliente.Id,
        documentNumber: cliente.DocumentNumber,
        code: verificationCode, // En producción NO logear el código
        ip: req.ip
      });

      // En desarrollo, devolver el código (NUNCA en producción)
      if (process.env.NODE_ENV === 'development') {
        return res.json({
          success: true,
          message: 'Código de verificación generado',
          code: verificationCode // Solo para desarrollo
        });
      }

      res.json({
        success: true,
        message: 'Código de verificación enviado al email/teléfono registrado'
      });

    } catch (error) {
      logger.error('Error al solicitar código de verificación', {
        error: error.message
      });
      res.status(500).json({
        success: false,
        message: 'Error en el servidor'
      });
    }
  };

  /**
   * Resetear contraseña con código de verificación
   */
  resetPasswordWithCode = async (req, res) => {
    try {
      const {
        documentNumber,
        verificationCode,
        newPassword
      } = req.body;

      // Validar campos
      if (!documentNumber || !verificationCode || !newPassword) {
        return res.status(400).json({
          success: false,
          message: 'Todos los campos son requeridos'
        });
      }

      // TODO: Verificar el código de verificación desde cache/base de datos

      // Buscar cliente
      const cliente = await Cliente.findOne({
        where: {
          DocumentNumber: documentNumber
        }
      });

      if (!cliente) {
        return res.status(404).json({
          success: false,
          message: 'Paciente no encontrado'
        });
      }

      // Hashear nueva contraseña
      const hashedPassword = await bcrypt.hash(
        newPassword,
        parseInt(process.env.BCRYPT_ROUNDS) || 10
      );

      // Actualizar contraseña
      await cliente.update({
        Password: hashedPassword
      });

      logger.auth('Contraseña reseteada con código - Cliente', {
        clienteId: cliente.Id,
        ip: req.ip
      });

      res.json({
        success: true,
        message: 'Contraseña reseteada exitosamente'
      });

    } catch (error) {
      logger.error('Error al resetear contraseña con código', {
        error: error.message
      });
      res.status(500).json({
        success: false,
        message: 'Error en el servidor'
      });
    }
  };

  /**
   * Verificar documento y enviar código en un solo paso
   */
  verifyAndSendCode = async (req, res) => {
    try {
      const {
        documentNumber,
        documentType
      } = req.body;

      if (!documentNumber) {
        return res.status(400).json({
          success: false,
          message: 'Número de documento es requerido'
        });
      }

      // Buscar cliente
      const whereClause = {
        DocumentNumber: documentNumber
      };
      if (documentType) {
        whereClause.DocumentType = documentType;
      }

      const cliente = await Cliente.findOne({
        where: whereClause
      });

      if (!cliente) {
        return res.status(404).json({
          success: false,
          message: 'No se encontró un paciente con esos datos'
        });
      }

      // Generar código
      const verificationCode = Math.floor(100000 + Math.random() * 900000).toString();

      // TODO: Enviar código por email/SMS

      logger.auth('Código de verificación enviado - Cliente', {
        clienteId: cliente.Id,
        documentNumber: cliente.DocumentNumber,
        ip: req.ip
      });

      res.json({
        success: true,
        message: 'Código de verificación enviado',
        data: {
          name: cliente.Name,
          email: cliente.Email.replace(/(.{2})(.*)(@.*)/, '\$1***\$3'),
          // Solo en desarrollo
          ...(process.env.NODE_ENV === 'development' && {
            code: verificationCode
          })
        }
      });

    } catch (error) {
      logger.error('Error al verificar y enviar código', {
        error: error.message
      });
      res.status(500).json({
        success: false,
        message: 'Error en el servidor'
      });
    }
  };
}

module.exports = new ClienteController();