import { Request, Response } from 'express';
import Appointments from '../../appointments/schema/appointments.schema';
import PaymentLink from '../../payment-links/schema/payment-link.schema';
import { logActivity } from '../../../helpers/activityLog.helper';
import { buildTextSearch, parsePagination, paginationMeta } from '../../../helpers/query.helper';
import {
  buildDocumentHtml,
  buildDocumentShareEmailText,
  buildDocumentShareSms,
} from '../../../helpers/document-template.helper';
import { buildDocumentShareEmailHtml } from '../../../helpers/notification-messages.helper';
import { getBusinessProfile } from '../../../helpers/site-settings.helper';
import { sendSms } from '../../../helpers/sms.helper';
import { sendTransactionalEmail } from '../../../helpers/emailer';

type DocumentType = 'invoice' | 'receipt';

function documentFields(type: string) {
  const docType: DocumentType = type === 'receipt' ? 'receipt' : 'invoice';
  return {
    numberField: docType === 'receipt' ? 'receiptNumber' : 'invoiceNumber',
    archivedField: docType === 'receipt' ? 'receiptArchivedAt' : 'invoiceArchivedAt',
    docType,
  };
}

async function resolvePaymentLink(appointment: { paymentLinkId?: string | null }) {
  if (!appointment.paymentLinkId) return null;
  const link = await PaymentLink.findById(appointment.paymentLinkId).lean();
  if (!link) return null;
  return {
    url: link.url,
    amount: link.amount,
    status: link.status,
  };
}

export class DocumentsController {
  static async list(req: Request, res: Response) {
    try {
      const type = String(req.query.type || 'invoice');
      const { page, limit, skip } = parsePagination(req, 10);
      const { numberField, archivedField } = documentFields(type);

      const filter: Record<string, unknown> = {
        [numberField]: { $ne: null },
        [archivedField]: null,
      };

      const search = buildTextSearch(
        ['fullName', 'email', 'phone', 'service', numberField],
        String(req.query.q || '')
      );
      if (search.$or) Object.assign(filter, search);

      const [items, total] = await Promise.all([
        Appointments.find(filter).sort({ updatedAt: -1 }).skip(skip).limit(limit),
        Appointments.countDocuments(filter),
      ]);

      return res.status(200).json({
        success: true,
        response: items,
        pagination: paginationMeta(page, limit, total),
      });
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }

  static async archive(req: Request, res: Response) {
    try {
      const { type, id } = req.params;
      const { numberField, archivedField, docType } = documentFields(type);

      const appointment = await Appointments.findById(id);
      if (!appointment || !appointment[numberField as keyof typeof appointment]) {
        return res.status(404).json({ success: false, message: 'Document not found' });
      }

      if (appointment[archivedField as keyof typeof appointment]) {
        return res.status(400).json({ success: false, message: 'Document already removed' });
      }

      (appointment as any)[archivedField] = new Date();
      await appointment.save();

      await logActivity(
        req,
        'ARCHIVE',
        docType.toUpperCase(),
        id,
        String(appointment[numberField as keyof typeof appointment])
      );

      return res.status(200).json({
        success: true,
        message: `${docType} removed from list. Record kept in database.`,
        response: appointment,
      });
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }

  static async preview(req: Request, res: Response) {
    try {
      const { type, id } = req.params;
      const appointment = await Appointments.findById(id).lean();
      if (!appointment) return res.status(404).json({ success: false, message: 'Not found' });

      const { docType, numberField } = documentFields(type);
      const business = await getBusinessProfile();
      const paymentLink = await resolvePaymentLink(appointment);
      const appointmentRecord = appointment as Record<string, unknown>;
      const messageContext = { appointment: appointmentRecord, business, paymentLink };
      const html = buildDocumentHtml(docType, appointmentRecord, business);
      const documentNumber = appointment[numberField as keyof typeof appointment];
      const shareEmailText = buildDocumentShareEmailText(docType, messageContext);
      const shareSmsText = buildDocumentShareSms(docType, messageContext);

      return res.status(200).json({
        success: true,
        response: {
          html,
          appointment,
          documentType: docType,
          documentNumber,
          shareText: shareEmailText,
          shareEmailText,
          shareSmsText,
        },
      });
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }

  static async download(req: Request, res: Response) {
    try {
      const { type, id } = req.params;
      const appointment = await Appointments.findById(id).lean();
      if (!appointment) return res.status(404).json({ success: false, message: 'Not found' });

      const { docType, numberField } = documentFields(type);
      const number = appointment[numberField as keyof typeof appointment];
      const business = await getBusinessProfile();
      const html = buildDocumentHtml(docType, appointment as Record<string, unknown>, business);

      await logActivity(req, 'DOWNLOAD', docType.toUpperCase(), id, String(number || ''));

      res.setHeader('Content-Type', 'text/html');
      res.setHeader('Content-Disposition', `attachment; filename="${number || docType}.html"`);
      return res.send(html);
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }

  static async send(req: Request, res: Response) {
    try {
      const channel = String(req.params.channel || '').toLowerCase();
      if (channel !== 'sms' && channel !== 'email') {
        return res.status(400).json({
          success: false,
          message: 'Supported delivery channels are sms and email',
        });
      }

      const { type, id } = req.params;
      const appointment = await Appointments.findById(id).lean();
      if (!appointment) {
        return res.status(404).json({ success: false, message: 'Document not found' });
      }

      const { docType, numberField } = documentFields(type);
      const documentNumber = appointment[numberField as keyof typeof appointment];
      if (!documentNumber) {
        return res.status(404).json({ success: false, message: 'Document not found' });
      }

      const business = await getBusinessProfile();
      const paymentLink = await resolvePaymentLink(appointment);
      const messageContext = {
        appointment: appointment as Record<string, unknown>,
        business,
        paymentLink,
      };

      if (channel === 'sms') {
        if (!appointment.phone) {
          return res.status(400).json({ success: false, message: 'Client phone number is required to send SMS' });
        }

        const message = buildDocumentShareSms(docType, messageContext);
        const sent = await sendSms(appointment.phone, message);

        if (!sent) {
          return res.status(502).json({
            success: false,
            message: 'Could not send SMS. Check SMS provider settings and try again.',
          });
        }

        await logActivity(req, 'SEND', `${docType.toUpperCase()}_SMS`, id, String(documentNumber));

        return res.status(200).json({
          success: true,
          message: `SMS sent to ${appointment.phone}`,
        });
      }

      const email = String(appointment.email || '').trim().toLowerCase();
      if (!email) {
        return res.status(400).json({ success: false, message: 'Client email is required to send email' });
      }

      const title = docType === 'receipt' ? 'Receipt' : 'Invoice';
      const subject = `${business.name} — ${title} ${documentNumber}`;
      const sent = await sendTransactionalEmail({
        to: email,
        subject,
        html: buildDocumentShareEmailHtml(docType, messageContext),
        text: buildDocumentShareEmailText(docType, messageContext),
      });

      if (!sent) {
        return res.status(502).json({
          success: false,
          message: 'Could not send email. Check mail server settings and try again.',
        });
      }

      await logActivity(req, 'SEND', `${docType.toUpperCase()}_EMAIL`, id, String(documentNumber));

      return res.status(200).json({
        success: true,
        message: `Email sent to ${email}`,
      });
    } catch {
      return res.status(500).json({ success: false, message: 'System error' });
    }
  }
}
