import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model } from 'mongoose'
import { randomUUID } from 'crypto'
import { Matter, MatterDocument, ClientFolder, ClientFolderDocument, LegalDocument, LegalDocumentDocument } from '../common/schemas'
import { FormTemplatesService } from '../form-templates/form-templates.service'
import { MatterDeadlinesService } from '../matter-deadlines/matter-deadlines.service'
import { CreateAnnualComplianceDto, DirectorDto, ShareholderDto } from './dto/create-annual-compliance.dto'

// ─── Types ────────────────────────────────────────────────────────────────────

export interface Director {
  uid: string
  nameEn: string
  nameTh: string
  email: string
  lineId?: string
  role: 'director' | 'chairman'
  signatureStatus: 'pending' | 'sent' | 'signed' | 'returned'
  sentAt?: Date
  signedAt?: Date
  returnedAt?: Date
}

export interface Shareholder {
  uid: string
  entityNameEn: string
  entityNameTh: string
  shareCount: number
  contactEmail: string
  lineId?: string
  proxyToUid?: string
  signatureStatus: 'pending' | 'sent' | 'signed' | 'returned'
  sentAt?: Date
  signedAt?: Date
  returnedAt?: Date
}

export interface ComplianceDocument {
  uid: string
  templateStockId: string
  label: string
  recipientType?: 'director' | 'shareholder' | 'all'
  recipientUid?: string
  recipientNameEn?: string
  generatedFormId?: string  // FormGenerationLog _id
  status: 'draft' | 'sent' | 'signed' | 'returned'
  sentAt?: Date
  signedAt?: Date
  returnedAt?: Date
  retentionCategory: 'lifetime' | '5_years' | '3_years'
}

export interface AnnualComplianceData {
  companyNameEn: string
  companyNameTh: string
  companyRegNumber?: string
  fiscalYear: string
  yearEndDate: string
  registeredAddressEn?: string
  registeredAddressTh?: string
  directors: Director[]
  shareholders: Shareholder[]
  bodMeeting?: { date?: string; time?: string; venue?: string }
  agmMeeting?: { date?: string; time?: string; venue?: string }
  agmDate?: string   // denormalized from agmMeeting.date for resolveAnchor compatibility
  auditorName?: string
  auditorFeeThb?: number
  feeThb: number
  vatIncluded: boolean
  documents: ComplianceDocument[]
}

// ─── Helpers ──────────────────────────────────────────────────────────────────

function formatDateEn(iso?: string): string {
  if (!iso) return '[DATE]'
  const d = new Date(iso)
  return d.toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })
}

function formatDateTh(iso?: string): string {
  if (!iso) return '[วันที่]'
  const d = new Date(iso)
  const be = d.getFullYear() + 543
  const month = d.toLocaleDateString('th-TH', { month: 'long' })
  return `${d.getDate()} ${month} ${be}`
}

function formatThb(amount?: number): string {
  if (!amount) return '0 บาท'
  return `${new Intl.NumberFormat('en').format(amount)} บาท (THB ${new Intl.NumberFormat('en').format(amount)})`
}

function buildBaseContext(data: AnnualComplianceData): Record<string, string> {
  const chairman = data.directors.find(d => d.role === 'chairman') ?? data.directors[0]
  const totalShares = data.shareholders.reduce((s, sh) => s + sh.shareCount, 0)
  const fiscalYearBE = String(parseInt(data.fiscalYear) + 543)

  const directorRows = data.directors.map((d, i) => `
    <tr>
      <td>${i + 1}</td>
      <td>${d.nameEn}<br><span style="font-size:11pt">${d.nameTh}</span></td>
      <td>${d.role === 'chairman' ? 'Chairman / ประธานกรรมการ' : 'Director / กรรมการ'}</td>
      <td style="height:50px"></td>
    </tr>`).join('')

  const shareholderRows = data.shareholders.map((s, i) => `
    <tr>
      <td>${i + 1}</td>
      <td>${s.entityNameEn}<br><span style="font-size:11pt">${s.entityNameTh}</span></td>
      <td class="right">${s.shareCount.toLocaleString('en')}</td>
      <td style="height:50px"></td>
      <td></td>
    </tr>`).join('')

  return {
    companyNameEn:       data.companyNameEn,
    companyNameTh:       data.companyNameTh,
    companyRegNumber:    data.companyRegNumber ?? '',
    fiscalYear:          data.fiscalYear,
    fiscalYearTh:        fiscalYearBE,
    registeredAddressEn: data.registeredAddressEn ?? '',
    registeredAddressTh: data.registeredAddressTh ?? '',
    bodDate:             formatDateEn(data.bodMeeting?.date),
    bodDateTh:           formatDateTh(data.bodMeeting?.date),
    bodTime:             data.bodMeeting?.time ?? '[TIME]',
    bodVenue:            data.bodMeeting?.venue ?? data.registeredAddressEn ?? '[VENUE]',
    agmDate:             formatDateEn(data.agmMeeting?.date),
    agmDateTh:           formatDateTh(data.agmMeeting?.date),
    agmTime:             data.agmMeeting?.time ?? '[TIME]',
    agmVenue:            data.agmMeeting?.venue ?? data.registeredAddressEn ?? '[VENUE]',
    chairmanEn:          chairman?.nameEn ?? '[CHAIRMAN]',
    chairmanTh:          chairman?.nameTh ?? '[ประธาน]',
    totalShares:         totalShares.toLocaleString('en'),
    auditorName:         data.auditorName ?? '[AUDITOR]',
    auditorFeeThb:       formatThb(data.auditorFeeThb),
    directorRows,
    shareholderRows,
  }
}

function buildDirectorContext(data: AnnualComplianceData, director: Director): Record<string, string> {
  return {
    ...buildBaseContext(data),
    directorName_En: director.nameEn,
    directorName_Th: director.nameTh,
  }
}

function buildShareholderContext(data: AnnualComplianceData, sh: Shareholder): Record<string, string> {
  const totalShares = data.shareholders.reduce((s, x) => s + x.shareCount, 0)
  const pct = totalShares > 0 ? ((sh.shareCount / totalShares) * 100).toFixed(2) : '0.00'
  const proxyHolder = sh.proxyToUid
    ? data.shareholders.find(s => s.uid === sh.proxyToUid)
    : null

  return {
    ...buildBaseContext(data),
    shName_En:    sh.entityNameEn,
    shName_Th:    sh.entityNameTh,
    shShares:     sh.shareCount.toLocaleString('en'),
    shPct:        pct,
    shProxyTo_En: proxyHolder ? proxyHolder.entityNameEn : '',
    shProxyTo_Th: proxyHolder ? proxyHolder.entityNameTh : '',
  }
}

// ─── Service ──────────────────────────────────────────────────────────────────

@Injectable()
export class AnnualComplianceService {
  constructor(
    @InjectModel(Matter.name)        private matterModel:  Model<MatterDocument>,
    @InjectModel(ClientFolder.name)  private clientModel:  Model<ClientFolderDocument>,
    @InjectModel(LegalDocument.name) private docModel:     Model<LegalDocumentDocument>,
    private readonly formTemplates:  FormTemplatesService,
    private readonly deadlines:      MatterDeadlinesService,
  ) {}

  // ── Create matter + store compliance data ────────────────────────────────

  async create(dto: CreateAnnualComplianceDto): Promise<MatterDocument> {
    const client = await this.clientModel.findById(dto.clientFolderId)
    if (!client) throw new NotFoundException(`ClientFolder ${dto.clientFolderId} not found`)

    const directors: Director[] = dto.directors.map(d => ({
      ...d,
      uid: d.uid ?? randomUUID(),
      signatureStatus: 'pending' as const,
    }))

    const shareholders: Shareholder[] = dto.shareholders.map(s => ({
      ...s,
      uid: s.uid ?? randomUUID(),
      signatureStatus: 'pending' as const,
    }))

    // Auto-generate matter number
    const year = new Date().getFullYear()
    const count = await this.matterModel.countDocuments()
    const matterNumber = `M-${year}-${String(count + 1).padStart(4, '0')}-CORP`

    const complianceData: AnnualComplianceData = {
      companyNameEn:       dto.companyNameEn,
      companyNameTh:       dto.companyNameTh,
      companyRegNumber:    dto.companyRegNumber,
      fiscalYear:          dto.fiscalYear,
      yearEndDate:         dto.yearEndDate,
      registeredAddressEn: dto.registeredAddressEn,
      registeredAddressTh: dto.registeredAddressTh,
      directors,
      shareholders,
      bodMeeting:  dto.bodMeeting ?? {},
      agmMeeting:  dto.agmMeeting ?? {},
      // Denormalized dates so resolveAnchor can find them at 'matter.annualCompliance.agmDate'
      agmDate:     dto.agmMeeting?.date,
      auditorName: dto.auditorName,
      auditorFeeThb: dto.auditorFeeThb,
      feeThb:      dto.feeThb ?? 15000,
      vatIncluded: dto.vatIncluded ?? false,
      documents:   [],
    }

    const matter = await this.matterModel.create({
      matterNumber,
      clientFolderId:       dto.clientFolderId,
      title:                `Annual Compliance ${dto.fiscalYear} — ${dto.companyNameEn}`,
      practiceArea:         'corporate',
      subType:              'annual_compliance',
      status:               'open',
      responsibleAttorney:  dto.responsibleAttorney,
      openedDate:           new Date(),
      currency:             'THB',
      totalBilled:          0,
      totalPaid:            0,
      notes:                dto.notes,
      // Store compliance-specific data in a dedicated namespace on the matter
      annualCompliance:     complianceData,
    } as any)

    // Auto-generate deadline proposals
    try {
      await this.deadlines.generateProposals(String((matter as any)._id))
    } catch {
      // Non-fatal — deadlines can be generated manually if this fails
    }

    return matter
  }

  // ── Fetch ────────────────────────────────────────────────────────────────

  async findOne(matterId: string): Promise<MatterDocument> {
    const matter = await this.matterModel.findById(matterId).lean() as any
    if (!matter) throw new NotFoundException(`Matter ${matterId} not found`)
    if (matter.subType !== 'annual_compliance') {
      throw new BadRequestException(`Matter ${matterId} is not an annual compliance matter`)
    }
    return matter
  }

  async findByClient(clientFolderId: string): Promise<MatterDocument[]> {
    return this.matterModel.find({ clientFolderId, subType: 'annual_compliance' })
      .sort({ createdAt: -1 }).lean() as any
  }

  // ── Generate all documents for a matter ─────────────────────────────────

  async generateAllDocuments(matterId: string, generatedBy?: string): Promise<ComplianceDocument[]> {
    const matter = await this.matterModel.findById(matterId)
    if (!matter) throw new NotFoundException(`Matter ${matterId} not found`)

    const data: AnnualComplianceData = (matter as any).annualCompliance
    if (!data) throw new BadRequestException('Matter has no annual compliance data')

    const docs: ComplianceDocument[] = []

    // ── Resolve template IDs from stock library ──────────────────────────
    const getTemplateId = async (stockId: string): Promise<string> => {
      const t = await (this.formTemplates as any).templateModel.findOne({ stockId }).lean() as any
      if (!t) throw new BadRequestException(
        `Stock template "${stockId}" not found. Run POST /form-templates/library/reseed first.`
      )
      return String(t._id)
    }

    const baseCtx = buildBaseContext(data)

    // Open a single browser for the entire batch — far faster than one browser per doc
    const browser = await this.formTemplates.openBrowser()
    const page = await browser.newPage()

    const gen = async (stockId: string, ctx: Record<string, string>) => {
      const tId = await getTemplateId(stockId)
      return this.formTemplates.generateFromHtml(tId, ctx, matterId, generatedBy, page)
    }

    try {

    // BOD Notice (1 doc)
    {
      const { logId } = await gen('th-corp-bod-notice', baseCtx)
      docs.push({
        uid: randomUUID(), templateStockId: 'th-corp-bod-notice',
        label: 'Notice of Board Meeting',
        recipientType: 'all', generatedFormId: logId,
        status: 'draft', retentionCategory: 'lifetime',
      })
    }

    // BOD Acknowledgment — one per director
    for (const director of data.directors) {
      const ctx = buildDirectorContext(data, director)
      const { logId } = await gen('th-corp-bod-acknowledgment', ctx)
      docs.push({
        uid: randomUUID(), templateStockId: 'th-corp-bod-acknowledgment',
        label: `BOD Acknowledgment — ${director.nameEn}`,
        recipientType: 'director', recipientUid: director.uid,
        recipientNameEn: director.nameEn, generatedFormId: logId,
        status: 'draft', retentionCategory: 'lifetime',
      })
    }

    // BOD Minutes (1 doc)
    { const { logId } = await gen('th-corp-bod-minutes', baseCtx); docs.push({ uid: randomUUID(), templateStockId: 'th-corp-bod-minutes', label: 'Minutes of Board Meeting', recipientType: 'all', generatedFormId: logId, status: 'draft', retentionCategory: 'lifetime' }) }

    // BOD Attendance (1 doc)
    { const { logId } = await gen('th-corp-bod-attendance', baseCtx); docs.push({ uid: randomUUID(), templateStockId: 'th-corp-bod-attendance', label: 'BOD Attendance Sheet', recipientType: 'all', generatedFormId: logId, status: 'draft', retentionCategory: 'lifetime' }) }

    // AGM Invitation (1 doc)
    { const { logId } = await gen('th-corp-agm-invitation', baseCtx); docs.push({ uid: randomUUID(), templateStockId: 'th-corp-agm-invitation', label: 'AGM Invitation Letter', recipientType: 'all', generatedFormId: logId, status: 'draft', retentionCategory: '5_years' }) }

    // Per-shareholder: Receipt + Proxy + Voting Sheet
    for (const sh of data.shareholders) {
      const ctx = buildShareholderContext(data, sh)
      const { logId: rLog } = await gen('th-corp-agm-receipt', ctx)
      docs.push({ uid: randomUUID(), templateStockId: 'th-corp-agm-receipt', label: `AGM Receipt — ${sh.entityNameEn}`, recipientType: 'shareholder', recipientUid: sh.uid, recipientNameEn: sh.entityNameEn, generatedFormId: rLog, status: 'draft', retentionCategory: '5_years' })
      const { logId: pLog } = await gen('th-corp-agm-proxy', ctx)
      docs.push({ uid: randomUUID(), templateStockId: 'th-corp-agm-proxy', label: `Proxy Form — ${sh.entityNameEn}`, recipientType: 'shareholder', recipientUid: sh.uid, recipientNameEn: sh.entityNameEn, generatedFormId: pLog, status: 'draft', retentionCategory: '5_years' })
      const { logId: vLog } = await gen('th-corp-agm-voting-sheet', ctx)
      docs.push({ uid: randomUUID(), templateStockId: 'th-corp-agm-voting-sheet', label: `Voting Sheet — ${sh.entityNameEn}`, recipientType: 'shareholder', recipientUid: sh.uid, recipientNameEn: sh.entityNameEn, generatedFormId: vLog, status: 'draft', retentionCategory: '5_years' })
    }

    // AGM Attendance (1 doc)
    { const { logId } = await gen('th-corp-agm-attendance', baseCtx); docs.push({ uid: randomUUID(), templateStockId: 'th-corp-agm-attendance', label: 'AGM Attendance Sheet', recipientType: 'all', generatedFormId: logId, status: 'draft', retentionCategory: '5_years' }) }

    // AGM Minutes (1 doc)
    { const { logId } = await gen('th-corp-agm-minutes', baseCtx); docs.push({ uid: randomUUID(), templateStockId: 'th-corp-agm-minutes', label: 'Minutes of Annual General Meeting', recipientType: 'all', generatedFormId: logId, status: 'draft', retentionCategory: 'lifetime' }) }

    } finally {
      // Always close the browser, even if generation fails partway through
      await page.close()
      await browser.close()
    }

    // Persist documents back to matter
    await this.matterModel.findByIdAndUpdate(matterId, {
      'annualCompliance.documents': docs,
    })

    return docs
  }

  // ── Per-document status update ───────────────────────────────────────────

  async updateDocumentStatus(
    matterId: string,
    docUid: string,
    status: 'draft' | 'sent' | 'signed' | 'returned',
  ): Promise<ComplianceDocument> {
    const matter = await this.matterModel.findById(matterId)
    if (!matter) throw new NotFoundException(`Matter ${matterId} not found`)

    const docs: ComplianceDocument[] = (matter as any).annualCompliance?.documents ?? []
    const idx = docs.findIndex(d => d.uid === docUid)
    if (idx === -1) throw new NotFoundException(`Document ${docUid} not found in matter`)

    const doc = docs[idx]
    doc.status = status
    if (status === 'sent')     doc.sentAt     = new Date()
    if (status === 'signed')   doc.signedAt   = new Date()
    if (status === 'returned') doc.returnedAt = new Date()

    await this.matterModel.findByIdAndUpdate(matterId, {
      'annualCompliance.documents': docs,
    })

    return doc
  }

  // ── Deadline summary ────────────────────────────────────────────────────

  async getDeadlines(matterId: string) {
    return this.deadlines.findByMatter(matterId)
  }

  // ── Dashboard summary: all annual compliance matters across all clients ──

  async dashboard(): Promise<any[]> {
    const matters = await this.matterModel
      .find({ subType: 'annual_compliance' })
      .sort({ createdAt: -1 })
      .lean() as any[]

    const now = Date.now()

    return matters.map(m => {
      const data: AnnualComplianceData = m.annualCompliance ?? {}
      const docs: ComplianceDocument[] = data.documents ?? []
      const total  = docs.length
      const signed = docs.filter(d => d.status === 'signed' || d.status === 'returned').length

      return {
        matterId:     String(m._id),
        matterNumber: m.matterNumber,
        companyNameEn: data.companyNameEn,
        companyNameTh: data.companyNameTh,
        fiscalYear:   data.fiscalYear,
        yearEndDate:  data.yearEndDate,
        agmDate:      data.agmMeeting?.date,
        status:       m.status,
        documentProgress: { total, signed },
        pendingSignatories: docs
          .filter(d => d.recipientNameEn && d.status !== 'signed' && d.status !== 'returned')
          .map(d => ({ name: d.recipientNameEn, label: d.label, status: d.status })),
      }
    })
  }
}
