import { Injectable, NotFoundException } from '@nestjs/common';
import { TenantConnectionService } from '../../database/tenant-connection.service';
import { LetterTemplate } from '../../database/tenant/entities/letter-template.entity';
import { WordGeneratorService } from './word-generator.service';

export interface CreateTemplateDto {
  code: string; title: string; titleNe?: string;
  category?: string; recordType?: string;
  body?: string; bodyNe?: string;
  variables?: string; isActive?: boolean; officeId?: string;
  filePath?: string; importTemplateName?: string;
}
export type UpdateTemplateDto = Partial<Omit<CreateTemplateDto, 'code'>>;

@Injectable()
export class LettersService {
  constructor(
    private tenantConn: TenantConnectionService,
    private wordGen: WordGeneratorService,
  ) {}
  private async repo(sub: string) {
    const ds = await this.tenantConn.getTenantDataSource(sub);
    return ds.getRepository(LetterTemplate);
  }

  async findAll(sub: string, opts: { category?: string; q?: string; page?: number; limit?: number }) {
    const r = await this.repo(sub);
    const { category, q, page = 1, limit = 20 } = opts;
    const qb = r.createQueryBuilder('l').where('l.deletedAt IS NULL').orderBy('l.category', 'ASC').addOrderBy('l.title', 'ASC').skip((page - 1) * limit).take(limit);
    if (category) qb.andWhere('l.category = :category', { category });
    if (q) qb.andWhere('(l.title LIKE :q OR l.titleNe LIKE :q OR l.code LIKE :q)', { q: `%${q}%` });
    const [items, total] = await qb.getManyAndCount();
    return { items, total, page, limit };
  }

  async findOne(sub: string, id: string) {
    const r = await this.repo(sub);
    const t = await r.findOne({ where: { id } });
    if (!t) throw new NotFoundException('Letter template not found');
    return t;
  }

  async create(sub: string, dto: CreateTemplateDto, userId?: string, userName?: string) {
    const r = await this.repo(sub);
    return r.save(r.create({ ...(dto as any), createdById: userId, createdByName: userName }));
  }

  async update(sub: string, id: string, dto: UpdateTemplateDto) {
    const r = await this.repo(sub);
    const t = await this.findOne(sub, id);
    Object.assign(t, dto);
    return r.save(t);
  }

  async remove(sub: string, id: string) {
    const r = await this.repo(sub);
    await this.findOne(sub, id);
    await r.softDelete(id);
    return { id };
  }

  async generateDocument(
    sub: string,
    templateId: string,
    data: {
      member?: Record<string, any>;
      application?: Record<string, any>;
      loan?: Record<string, any>;
      guarantor?: Record<string, any>;
      office?: Record<string, any>;
      extraVariables?: Record<string, string>;
      exportDate?: string;
    },
  ): Promise<{ buffer: Buffer; filename: string }> {
    const template = await this.findOne(sub, templateId);

    const dateVars   = this.wordGen.buildDateVariables(data.exportDate);
    const officeVars = this.wordGen.buildOfficeVariables(data.office ?? null);
    const memberVars = this.wordGen.buildMemberVariables(data.member ?? null);
    const appVars    = this.wordGen.buildApplicationVariables(data.application ?? null);
    const loanVars   = this.wordGen.buildLoanVariables(data.loan ?? null);
    const gVars      = this.wordGen.buildGuarantorVariables(data.guarantor ?? null);

    const merged = this.wordGen.mergeVariables(
      dateVars, officeVars, memberVars, appVars, loanVars, gVars,
      data.extraVariables ?? {},
    );

    const filename = `${template.importTemplateName ?? template.code}_${Date.now()}.docx`;

    if (template.filePath) {
      const buffer = await this.wordGen.generateDocxFromPath(template.filePath, merged);
      return { buffer, filename };
    }

    const body = template.body ?? '';
    const rendered = body.replace(/\$\{(\w+)\}|\{\{(\w+)\}\}/g, (_m: string, k1: string, k2: string) => {
      const key = k1 || k2;
      return merged[key] ?? '';
    });

    const textBuffer = Buffer.from(rendered, 'utf-8');
    return { buffer: textBuffer, filename: filename.replace('.docx', '.txt') };
  }

  async seedDefaultTemplates(sub: string) {
    const r = await this.repo(sub);
    const count = await r.count();
    if (count > 0) return;
    const defaults: CreateTemplateDto[] = [
      {
        code: 'LOAN_DEMAND', title: 'Loan Demand Notice', titleNe: 'ऋण माग सूचना',
        category: 'demand', body: 'Dear {{memberName}},\n\nThis is to inform you that your loan account {{loanNumber}} has an outstanding balance of NPR {{outstanding}}.\n\nPlease clear your dues within 7 days.\n\nThank you.',
        bodyNe: 'प्रिय {{memberName}},\n\nसूचित गरिन्छ कि तपाईंको ऋण खाता {{loanNumber}} मा NPR {{outstanding}} बाँकी छ।\n\nकृपया ७ दिनभित्र चुक्ता गर्नुहोला।\n\nधन्यवाद।',
        variables: 'memberName,loanNumber,outstanding',
      },
      {
        code: 'MEMBER_WELCOME', title: 'Member Welcome Letter', titleNe: 'सदस्य स्वागत पत्र',
        category: 'member', body: 'Dear {{memberName}},\n\nWelcome to our cooperative! Your membership number is {{memberId}}.\n\nWe are glad to have you as our member.',
        bodyNe: 'प्रिय {{memberName}},\n\nहाम्रो सहकारीमा स्वागत छ! तपाईंको सदस्य नम्बर {{memberId}} हो।\n\nतपाईंलाई सदस्यको रूपमा पाउँदा खुसी छौं।',
        variables: 'memberName,memberId',
      },
      {
        code: 'SAVINGS_MATURITY', title: 'Savings Maturity Notice', titleNe: 'बचत परिपक्वता सूचना',
        category: 'savings', body: 'Dear {{memberName}},\n\nYour savings account {{accountNumber}} will mature on {{maturityDate}}.\n\nCurrent balance: NPR {{balance}}. Please visit our office.',
        bodyNe: 'प्रिय {{memberName}},\n\nतपाईंको बचत खाता {{accountNumber}} {{maturityDate}} मा परिपक्व हुनेछ।\n\nहालको मौज्दात: NPR {{balance}}। कृपया कार्यालयमा आउनुहोस्।',
        variables: 'memberName,accountNumber,maturityDate,balance',
      },
      {
        code: 'MEETING_NOTICE', title: 'Meeting Notice', titleNe: 'बैठक सूचना',
        category: 'notice', body: 'Dear {{memberName}},\n\nYou are cordially invited to attend the {{meetingType}} meeting scheduled on {{meetingDate}} at {{venue}}.\n\nAgenda: {{agenda}}',
        bodyNe: 'प्रिय {{memberName}},\n\n{{meetingDate}} मा {{venue}} मा हुने {{meetingType}} बैठकमा उपस्थित हुन अनुरोध गरिन्छ।\n\nएजेन्डा: {{agenda}}',
        variables: 'memberName,meetingType,meetingDate,venue,agenda',
      },
    ];
    for (const d of defaults) {
      await r.save(r.create(d as any)).catch(() => null);
    }
  }
}
