import { Request, Response } from 'express';
import { prisma } from '../models';
import {
  LicenseStatus,
  LicenseType,
  DocumentType,
  PitStatus,
  Prisma,
  Role,
} from '@prisma/client';
import { writeAudit } from '../lib/audit';
import { computeDaysRemaining } from '../lib/days-remaining';
import {
  cascadeDisablePitsForLicense,
  onLicenseSuspendedOrExpired,
  runLicenseExpiryCheck,
} from '../middleware/license-kill-switch';
import { CloudStorageService } from '../services/cloud-storage.service';

function parseLicenseBody(req: Request): Record<string, unknown> {
  if (typeof req.body?.payload === 'string') {
    try {
      return JSON.parse(req.body.payload) as Record<string, unknown>;
    } catch {
      return {};
    }
  }
  return req.body as Record<string, unknown>;
}

export const licenseController = {
  async list(req: Request, res: Response) {
    const page = Math.max(1, parseInt(String(req.query.page || '1'), 10) || 1);
    const limit = Math.min(100, Math.max(1, parseInt(String(req.query.limit || '20'), 10) || 20));
    const type = req.query.type as LicenseType | undefined;
    const status = req.query.status as LicenseStatus | undefined;
    const expiring = req.query.expiry === 'soon';

    const where: Prisma.LicenseWhereInput = { deleted_at: null };
    if (type) where.license_type = type;
    if (status) where.status = status;
    if (expiring) {
      const d = new Date();
      const end = new Date(d);
      end.setDate(d.getDate() + 30);
      where.expiry_date = { lte: end, gte: d };
    }

    const [total, licenses] = await Promise.all([
      prisma.license.count({ where }),
      prisma.license.findMany({
        where,
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { expiry_date: 'asc' },
        include: {
          _count: {
            select: {
              pits: { where: { deleted_at: null } },
              documents: { where: { deleted_at: null } },
            },
          },
        },
      }),
    ]);

    const data = licenses.map((l) => ({
      id: l.id,
      license_number: l.license_number,
      license_name: l.license_name,
      license_type: l.license_type,
      status: l.status,
      expiry_date: l.expiry_date.toISOString().slice(0, 10),
      days_remaining: computeDaysRemaining(l.expiry_date),
      pits_count: l._count.pits,
      document_count: l._count.documents,
    }));

    res.json({
      success: true,
      data: {
        licenses: data,
        pagination: { page, limit, total },
      },
    });
  },

  async getOne(req: Request, res: Response) {
    const { id } = req.params;
    const license = await prisma.license.findFirst({
      where: { id, deleted_at: null },
      include: {
        pits: { where: { deleted_at: null } },
        documents: { where: { deleted_at: null } },
        jv_partners: true,
      },
    });
    if (!license) {
      res.status(404).json({ success: false, error: 'License not found' });
      return;
    }
    const days_remaining = computeDaysRemaining(license.expiry_date);
    res.json({
      success: true,
      data: { ...license, days_remaining },
    });
  },

  async create(req: Request, res: Response) {
    const b = parseLicenseBody(req);
    const files = req.files as Record<string, Express.Multer.File[]> | undefined;

    const license_type = b.license_type as LicenseType;
    const license_number = String(b.license_number ?? '');
    const license_name = String(b.license_name ?? '');
    const region = String(b.region ?? '');
    const district = String(b.district ?? '');
    const ward = String(b.ward ?? '');
    const village = String(b.village ?? '');
    const area_hectares = Number(b.area_hectares);
    const issue_date = new Date(String(b.issue_date));
    const expiry_date = new Date(String(b.expiry_date));
    const issuing_authority = String(b.issuing_authority ?? '');
    const wmcl_ownership_percent = Number(b.wmcl_ownership_percent ?? 100);
    const gps_center_lat = b.gps_center_lat != null ? Number(b.gps_center_lat) : undefined;
    const gps_center_lng = b.gps_center_lng != null ? Number(b.gps_center_lng) : undefined;

    if (!license_type || !license_number || !license_name) {
      res.status(400).json({ success: false, error: 'Missing required license fields' });
      return;
    }

    const pmlFile = files?.pml_certificate?.[0];
    const jvFile = files?.jv_contract?.[0];
    const base64Pml = b.pml_certificate_base64 as string | undefined;

    if (!pmlFile && !base64Pml) {
      res.status(400).json({ success: false, error: 'PML_CERTIFICATE is required' });
      return;
    }
    if (wmcl_ownership_percent < 100 && !jvFile) {
      res.status(400).json({
        success: false,
        error: 'JV_CONTRACT is required when wmcl_ownership_percent < 100',
      });
      return;
    }

    const uploadedBy = req.user!.id;

    const license = await prisma.license.create({
      data: {
        license_type,
        license_number,
        license_name,
        region,
        district,
        ward,
        village,
        gps_center_lat,
        gps_center_lng,
        area_hectares,
        issue_date,
        expiry_date,
        issuing_authority,
        wmcl_ownership_percent,
        status: LicenseStatus.ACTIVE,
        days_remaining: computeDaysRemaining(expiry_date),
      },
    });

    const docs: { type: DocumentType; file: Express.Multer.File | null; base64?: string; mime?: string }[] = [
      { type: DocumentType.PML_CERTIFICATE, file: pmlFile ?? null, base64: base64Pml, mime: 'application/pdf' },
    ];
    if (jvFile) docs.push({ type: DocumentType.JV_CONTRACT, file: jvFile, base64: undefined });

    for (const d of docs) {
      let buf: Buffer;
      let fileName: string;
      let mime: string;
      if (d.file) {
        buf = d.file.buffer;
        fileName = d.file.originalname;
        mime = d.file.mimetype;
      } else if (d.base64 && d.type === DocumentType.PML_CERTIFICATE) {
        buf = Buffer.from(d.base64, 'base64');
        fileName = 'pml_certificate.pdf';
        mime = d.mime ?? 'application/pdf';
      } else continue;

      const url = await CloudStorageService.upload(
        buf,
        `licenses/${license.id}/${d.type}_${fileName}`,
        mime
      );
      await prisma.document.create({
        data: {
          license_id: license.id,
          document_type: d.type,
          file_name: fileName,
          file_url: url,
          file_size: buf.length,
          mime_type: mime,
          uploaded_by: uploadedBy,
          is_required: true,
        },
      });
    }

    await writeAudit(req, 'CREATE', 'License', license.id, undefined, license as unknown as Prisma.JsonValue);

    res.status(201).json({ success: true, data: license });
  },

  async update(req: Request, res: Response) {
    const { id } = req.params;
    const existing = await prisma.license.findFirst({ where: { id, deleted_at: null } });
    if (!existing) {
      res.status(404).json({ success: false, error: 'Not found' });
      return;
    }

    const b = req.body as Record<string, unknown>;
    const isMd = req.user!.role === Role.MD;
    if (!isMd && (b.wmcl_ownership_percent != null || b.issuing_authority != null)) {
      res.status(403).json({ success: false, error: 'Only MD can change ownership or issuing authority' });
      return;
    }

    const data: Prisma.LicenseUpdateInput = {};
    if (b.license_name != null) data.license_name = String(b.license_name);
    if (b.region != null) data.region = String(b.region);
    if (b.district != null) data.district = String(b.district);
    if (b.ward != null) data.ward = String(b.ward);
    if (b.village != null) data.village = String(b.village);
    if (b.gps_center_lat != null) data.gps_center_lat = Number(b.gps_center_lat);
    if (b.gps_center_lng != null) data.gps_center_lng = Number(b.gps_center_lng);
    if (b.gps_boundary_url != null) data.gps_boundary_url = String(b.gps_boundary_url);
    if (b.area_hectares != null) data.area_hectares = Number(b.area_hectares);
    if (b.issue_date != null) data.issue_date = new Date(String(b.issue_date));
    if (b.expiry_date != null) data.expiry_date = new Date(String(b.expiry_date));
    if (b.wmcl_ownership_percent != null && isMd) data.wmcl_ownership_percent = Number(b.wmcl_ownership_percent);
    if (b.issuing_authority != null && isMd) data.issuing_authority = String(b.issuing_authority);

    const updated = await prisma.license.update({
      where: { id },
      data: {
        ...data,
        days_remaining:
          data.expiry_date != null
            ? computeDaysRemaining(data.expiry_date as Date)
            : computeDaysRemaining(existing.expiry_date),
      },
    });

    await writeAudit(req, 'UPDATE', 'License', id, existing as unknown as Prisma.JsonValue, updated as unknown as Prisma.JsonValue);
    res.json({ success: true, data: updated });
  },

  async softDelete(req: Request, res: Response) {
    const { id } = req.params;
    const now = new Date();
    await prisma.license.update({
      where: { id },
      data: { deleted_at: now, deleted_by: req.user!.id },
    });
    await writeAudit(req, 'DELETE', 'License', id);
    res.json({ success: true, message: 'License soft-deleted' });
  },

  async suspend(req: Request, res: Response) {
    const { id } = req.params;
    const lic = await prisma.license.update({
      where: { id },
      data: { status: LicenseStatus.SUSPENDED },
    });
    await onLicenseSuspendedOrExpired(id, LicenseStatus.SUSPENDED);
    await writeAudit(req, 'SUSPEND', 'License', id);
    res.json({ success: true, data: lic });
  },

  async renew(req: Request, res: Response) {
    const { id } = req.params;
    const expiry_date = new Date(String((req.body as { expiry_date: string }).expiry_date));
    const lic = await prisma.$transaction(async (tx) => {
      const l = await tx.license.update({
        where: { id },
        data: {
          expiry_date,
          status: LicenseStatus.ACTIVE,
          days_remaining: computeDaysRemaining(expiry_date),
        },
      });
      await tx.pit.updateMany({
        where: { license_id: id, deleted_at: null },
        data: { status: PitStatus.ACTIVE, operational_access: true },
      });
      return l;
    });
    await writeAudit(req, 'RENEW', 'License', id);
    res.json({ success: true, data: lic });
  },

  async restore(req: Request, res: Response) {
    const { id } = req.params;
    const existing = await prisma.license.findUnique({ where: { id } });
    if (!existing) {
      res.status(404).json({ success: false, error: 'Not found' });
      return;
    }
    const dr = computeDaysRemaining(existing.expiry_date);
    const lic = await prisma.$transaction(async (tx) => {
      const l = await tx.license.update({
        where: { id },
        data: {
          status: LicenseStatus.ACTIVE,
          deleted_at: null,
          deleted_by: null,
          days_remaining: dr,
        },
      });
      if (dr >= 0) {
        await tx.pit.updateMany({
          where: { license_id: id, deleted_at: null },
          data: { status: PitStatus.ACTIVE, operational_access: true },
        });
      }
      return l;
    });
    await writeAudit(req, 'RESTORE', 'License', id, undefined, undefined, req.body?.md_otp);
    res.json({ success: true, data: lic });
  },

  async cronCheck(req: Request, res: Response) {
    const secret = req.headers['x-cron-secret'];
    if (secret !== process.env.CRON_SECRET) {
      res.status(403).json({ success: false, error: 'Forbidden' });
      return;
    }
    const result = await runLicenseExpiryCheck();
    res.json({ success: true, data: result });
  },
};
