import {
  Injectable,
  BadRequestException,
  NotFoundException,
} from '@nestjs/common';
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource, In } from 'typeorm';
import { Job } from './entities/job.entity';
import { Company } from '../companies/entities/company.entity';
import { CompanyAuditStage } from '../company-audit-stages/entities/company-audit-stage.entity';
import { Template } from '../template/entities/template.entity';
import { Standard } from '../standards/entities/standard.entity';
import { User } from '../user/entities/user.entity';
import { CreateJobDto } from './dto/create-job.dto';
import { plainToInstance } from 'class-transformer';
import { UserDto } from '../user/dto/user.dto';

@Injectable()
export class JobsService {
  constructor(
    @InjectRepository(Job, 'certification_db')
    private readonly jobRepo: Repository<Job>,

    @InjectRepository(Company, 'certification_db')
    private readonly companyRepo: Repository<Company>,

    @InjectRepository(CompanyAuditStage, 'certification_db')
    private readonly stageRepo: Repository<CompanyAuditStage>,

    @InjectRepository(Template, 'certification_db')
    private readonly templateRepo: Repository<Template>,

    @InjectRepository(Standard, 'certification_db')
    private readonly standardRepo: Repository<Standard>,

    @InjectRepository(User, 'certification_db')
    private readonly userRepo: Repository<User>,

    @InjectRepository(Job, 'certification_db')
    private readonly jobRepository: Repository<Job>,

    @InjectDataSource('certification_db')
    private readonly dataSource: DataSource,
  ) {}

  // <-- ADD HERE
  async getNextJobNumber(): Promise<string> {
    const date = new Date();
    const month = (date.getMonth() + 1).toString().padStart(2, '0'); // 01-12

    // Starting number
    const INITIAL_NUMBER = 96148; // corresponds to 0096148

    // Get the last job's id or last numeric job number
    const lastJob = await this.jobRepo
      .createQueryBuilder('job')
      .orderBy('id', 'DESC')
      .getOne();

    let nextNumber = INITIAL_NUMBER;

    if (lastJob) {
      nextNumber = lastJob.id + 1; // increment from last job id
    }

    // Pad to 6 digits to match your format
    const paddedNumber = nextNumber.toString().padStart(6, '0');

    // Add month prefix
    return `${month}${paddedNumber}`;
  }
  // Initial numbers per standard
  private readonly INITIAL_NUMBERS: Record<string, number> = {
    QMS: 96148,
    EMS: 43138,
    OHSMS: 6129,
    FSMS: 168,
    HACCP: 140,
    ISMS: 7,
    ISO22000: 7,
    HALAL: 10,
    GMP: 13,
    ISO22301: 19,
    ISO50001: 8,
    ISO29001: 1,
    ISO9120: 1,
    ISO37001: 2,
    ISO21001: 1,
    ISO21500: 1,
    ISO13485: 4,
    HACAP: 1,
  };

  private readonly PREFIX_MAP: Record<string, string> = {
    'ISO 9001, 14001 & 45001': 'QMS_EMS_OHSMS',
    'ISO 9001': 'QMS',
    'ISO 9001:2015': 'QMS',
    'ISO 14001:2015': 'EMS',
    'ISO 45001:2018': 'OHSMS',
    // 'ISO 27001:2013': 'IMS',
    'ISO 22000:2018': 'FSMS',
    'ISO 41001:2018': 'HALAL',
    'ISO22301:2019': 'ISO22301',
    'ISO50001:2018': 'ISO50001',
    'ISO29001:2020': 'ISO29001',
    'ISO 27001:2013': 'ISMS',
    'ISO 21001: 2018': 'ISO21001',
    HACCP: 'HACCP',
  };

  // Generate job code per standard and month
  private async generateJobCodeForStandard(
    standardPrefix: string,
  ): Promise<string> {
    const now = new Date();
    let monthNumber = now.getMonth() + 1;

    // 🔒 Force October start
    if (monthNumber < 10) monthNumber = 10;

    const month = monthNumber.toString().padStart(2, '0');

    // 🔥 IMPORTANT FIX: ignore month while searching last number
    const lastJob = await this.jobRepo
      .createQueryBuilder('job')
      .where('job.jobCode LIKE :prefix', {
        prefix: `%${standardPrefix}:%-%`,
      })
      .orderBy('job.id', 'DESC')
      .getOne();

    let nextNumber = this.INITIAL_NUMBERS[standardPrefix] || 1000;

    if (lastJob) {
      const codes = lastJob.jobCode.split(',').map((c) => c.trim());
      const codeForStandard = codes.find((c) =>
        c.startsWith(`${standardPrefix}:`),
      );

      if (codeForStandard) {
        const parsed = parseInt(codeForStandard.split('-')[1], 10);
        if (!isNaN(parsed)) {
          nextNumber = parsed + 1;
        }
      }
    }

    return `${standardPrefix}:${month}-${nextNumber}`;
  }

  async create(dto: CreateJobDto) {
    const company = await this.companyRepo.findOne({
      where: { id: dto.companyId },
    });
    if (!company) throw new NotFoundException('Company not found');

    // Fetch template with contents but avoid circular serialization issues
    const template = await this.templateRepo.findOne({
      where: { id: dto.templateId },
      relations: ['contents'],
    });
    if (!template) throw new NotFoundException('Template not found');

    const standards = await this.standardRepo.findBy({ id: In(dto.standards) });
    if (!standards.length)
      throw new NotFoundException('No valid standards found');

    const stage = await this.stageRepo.findOne({
      where: { id: dto.auditStageId },
    });
    if (!stage) throw new NotFoundException('Audit stage not found');

    const leadAuditor = await this.userRepo.findOne({
      where: { id: dto.leadAuditorId },
    });
    if (!leadAuditor) throw new NotFoundException('Lead auditor not found');

    // Generate job codes for all standards
    const jobCodes = await Promise.all(
      standards.map(async (standard) => {
        const prefix = this.PREFIX_MAP[standard.name];
        if (!prefix)
          throw new BadRequestException(
            `Missing prefix mapping for standard: ${standard.name}`,
          );
        if (!this.INITIAL_NUMBERS[prefix])
          throw new BadRequestException(
            `Missing initial number for standard prefix: ${prefix}`,
          );
        return this.generateJobCodeForStandard(prefix);
      }),
    );

    // --- TEMPLATE SNAPSHOT ---
    const templateSnapshot = {
      id: template.id,
      name: template.name,
      stageName: template.stageName,
      version: template.version,
      filePath: template.filePath,
      isActive: template.isActive,
      contents: template.contents ?? [],
    };

    // --- COMPANY SNAPSHOT ---
    const companySnapshot = {
      companyName: company.name,
      companyAddress: company.address,
      companyScope: company.scope_of_work,
      companycity: company.city,
      companycontact_person: company.contact_person,
      companydesignation: company.designation,
      companyemail: company.email,
      companymobile: company.mobile,
      companytelephone: company.telephone,
      companyFax: company.fax,
      companyvalidity: company.validity,
      companyaccreditation: company.accreditation,
      companycertification_body: company.accreditation,
      companyRefrence: company.reference_number,
    };

    // Create Job entity
    const job = this.jobRepo.create({
      jobCode: jobCodes.join(', '),
      company,
      auditStage: stage,
      template,
      templateSnapshot, // <-- template snapshot
      companySnapshot, // <-- new company snapshot
      standards,
      stage: dto.stage,
      leadAuditor,
      date: new Date(dto.date),
      docReview: new Date(dto.date),
      numEmployees: dto.numEmployees,
      naceEacCodes: dto.naceEacCodes,
      md: dto.md,
      mdRisk: dto.mdRisk,
      scheduleSlot: dto.scheduleSlot || 'FULL',
      expectedSurveillanceDate: dto.expectedSurveillanceDate,
      expectedIADate: dto.expectedIADate,
      expectedMRMDate: dto.expectedMRMDate,
      expectedInitialInquiryDate: dto.expectedInitialInquiryDate,
      expectedInitial_Stage1_Date: dto.expectedInitial_Stage1_Date,
      prepareDate: dto.prepareDate,
      approvedDate: dto.approvedDate,
    });

    // Save to DB
    const savedJob = await this.jobRepo.save(job);
    console.log('Job saved:', savedJob.id);

    return {
      id: savedJob.id,
      jobCodes,
      stage: savedJob.stage,
      stages: [
        {
          stageName: savedJob.stage,
          md: savedJob.md,
          docReview: savedJob.docReview,
          date: savedJob.date,
          auditBy: savedJob.leadAuditor,
        },
      ],

      template: templateSnapshot, // return template snapshot
      companySnapshot, // return company snapshot
      standards: savedJob.standards.map((s) => ({
        id: s.id,
        name: s.name,
        title: s.title,
      })),
      company: {
        id: savedJob.company.id,
        name: savedJob.company.name,
        address: savedJob.company.address,
        scope_of_work: savedJob.company.scope_of_work,
        city: savedJob.company.city,
        contact_person: savedJob.company.contact_person,
        designation: savedJob.company.designation,
        email: savedJob.company.email,
        mobile: savedJob.company.mobile,
        telephone: savedJob.company.telephone,
        fax: savedJob.company.fax,
        validity: savedJob.company.validity,
        accreditation: savedJob.company.accreditation,
        certification_body: savedJob.company.accreditation,
        reference_number: savedJob.company.reference_number,
      },
      date: savedJob.date,
      docReview: savedJob.docReview,
      numEmployees: savedJob.numEmployees,
      naceEacCodes: savedJob.naceEacCodes,
      md: savedJob.md,
      mdRisk: savedJob.mdRisk,
      createdAt: savedJob.createdAt,
      updatedAt: savedJob.updatedAt,

      expectedSurveillanceDate: savedJob.expectedSurveillanceDate,
      expectedIADate: savedJob.expectedIADate,
      expectedMRMDate: savedJob.expectedMRMDate,
      expectedInitialInquiryDate: savedJob.expectedInitialInquiryDate,
      expectedInitial_Stage1_Date: savedJob.expectedInitial_Stage1_Date,
      prepareDate: savedJob.prepareDate,
      approvedDate: savedJob.approvedDate,
    };
  }

  async findAll({ page = 1, limit = 10 }: { page?: number; limit?: number } = {}) {
  const skip = (page - 1) * limit;

  const [jobs, total] = await this.jobRepo.findAndCount({
    relations: [
      'company',
      'template',
      'template.contents',
      'standards',
      'auditStage',
      'leadAuditor',
    ],
    order: { createdAt: 'DESC' }, // ✅ latest first
    skip,
    take: limit,
  });

  const data = jobs.map((job) => {
    const snapshot = job.templateSnapshot ?? {
      id: job.template.id,
      name: job.template.name,
      stageName: job.template.stageName,
      version: job.template.version,
      filePath: job.template.filePath,
      isActive: job.template.isActive,
      contents: job.template.contents ?? [],
    };

    const pdfUrl = job.pdfPath
      ? job.pdfPath
      : `/api/pdf/job/${job.jobCode}/${snapshot.stageName
          .toLowerCase()
          .replace(/\s+/g, '')}/${snapshot.filePath}?companyId=${job.company.id}`;

    const companySnapshot = job.companySnapshot ?? {
      companyName: job.company.name,
      companyAddress: job.company.address,
      companyScope: job.company.scope_of_work,
      companycity: job.company.city,
      companycontact_person: job.company.contact_person,
      companydesignation: job.company.designation,
      companyemail: job.company.email,
      companymobile: job.company.mobile,
      companytelephone: job.company.telephone,
      companyFax: job.company.fax,
      companyvalidity: job.company.validity,
      companyaccreditation: job.company.accreditation,
      companycertification_body: job.company.accreditation,
    };

    return {
      company: { id: job.company.id, name: job.company.name },
      id: job.id,
      jobCodes: job.jobCode.split(',').map((code) => code.trim()),
      stage: job.stage,
      stages: [
        {
          stageName: job.stage,
          md: job.md,
          docReview: job.docReview,
          date: job.date,
          auditBy: job.leadAuditor,
        },
      ],
      template: { ...snapshot, url: pdfUrl },
      companySnapshot,
      standards: job.standards.map((s) => ({
        id: s.id,
        name: s.name,
        title: s.title,
      })),
      leadAuditor: plainToInstance(UserDto, job.leadAuditor, {
        excludeExtraneousValues: true,
      }),
      date: job.date,
      docReview: job.docReview,
      numEmployees: job.numEmployees,
      naceEacCodes: job.naceEacCodes,
      md: job.md,
      mdRisk: job.mdRisk,
      scheduleSlot: job.scheduleSlot,
      expectedSurveillanceDate: job.expectedSurveillanceDate,
      expectedIADate: job.expectedIADate,
      expectedMRMDate: job.expectedMRMDate,
      expectedInitialInquiryDate: job.expectedInitialInquiryDate,
      expectedInitial_Stage1_Date: job.expectedInitial_Stage1_Date,
      prepareDate: job.prepareDate,
      approvedDate: job.approvedDate,
      createdAt: job.createdAt,
      updatedAt: job.updatedAt,
    };
  });

  // ✅ Return paginated envelope
  return {
    data,
    meta: {
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
      hasNextPage: page < Math.ceil(total / limit),
      hasPrevPage: page > 1,
    },
  };
}
  async findAllWithFilters(filters: {
    companyName?: string;
    templateName?: string;
    stage?: string;
    startDate?: string;
    endDate?: string;
  }) {
    const qb = this.jobRepo
      .createQueryBuilder('job')
      .leftJoinAndSelect('job.company', 'company')
      .leftJoinAndSelect('job.template', 'template')
      .leftJoinAndSelect('template.contents', 'contents')
      .leftJoinAndSelect('job.standards', 'standards')
      .leftJoinAndSelect('job.auditStage', 'auditStage')
      .leftJoinAndSelect('job.leadAuditor', 'leadAuditor');

    if (filters.companyName) {
      qb.andWhere('company.name ILIKE :companyName', {
        companyName: `%${filters.companyName}%`,
      });
    }

    if (filters.templateName) {
      qb.andWhere('template.name ILIKE :templateName', {
        templateName: `%${filters.templateName}%`,
      });
    }

    if (filters.stage) {
      qb.andWhere('job.stage = :stage', { stage: filters.stage });
    }

    if (filters.startDate) {
      qb.andWhere('job.date >= :startDate', {
        startDate: new Date(filters.startDate),
      });
    }

    if (filters.endDate) {
      qb.andWhere('job.date <= :endDate', {
        endDate: new Date(filters.endDate),
      });
    }

    // Latest first — same sort as the React component
    qb.orderBy('job.date', 'DESC');

    const jobs = await qb.getMany();

    // Re-use the same shape as findAll() so PDF/Excel services get consistent data
    return jobs.map((job) => {
      const snapshot = job.templateSnapshot ?? {
        id: job.template?.id,
        name: job.template?.name,
        stageName: job.template?.stageName,
        version: job.template?.version,
        filePath: job.template?.filePath,
        isActive: job.template?.isActive,
        contents: job.template?.contents ?? [],
      };

      return {
        id: job.id,
        jobCodes: job.jobCode.split(',').map((c) => c.trim()),
        company: { id: job.company.id, name: job.company.name },
        stage: job.stage,
        template: snapshot,
        standards: (job.standards || []).map((s) => ({
          id: s.id,
          name: s.name,
          title: s.title,
        })),
        leadAuditor: job.leadAuditor
          ? {
              id: job.leadAuditor.id,
              firstName: job.leadAuditor.firstName,
              lastName: job.leadAuditor.lastName,
            }
          : null,
        date: job.date,
        docReview: job.docReview,
        numEmployees: job.numEmployees,
        naceEacCodes: job.naceEacCodes,
        md: job.md,
        mdRisk: job.mdRisk,
        scheduleSlot: job.scheduleSlot,
        createdAt: job.createdAt,
        updatedAt: job.updatedAt,
      };
    });
  }
  async findOne(id: number) {
    const job = await this.jobRepo.findOne({
      where: { id },
      relations: [
        'company',
        'template',
        'template.contents',
        'standards',
        'auditStage',
        'leadAuditor',
      ],
    });

    if (!job) throw new NotFoundException('Job not found');

    const snapshot = job.templateSnapshot ?? {
      id: job.template.id,
      name: job.template.name,
      stageName: job.template.stageName,
      version: job.template.version,
      filePath: job.template.filePath,
      isActive: job.template.isActive,
      contents: job.template.contents ?? [],
    };

    const pdfUrl = job.pdfPath
      ? job.pdfPath
      : `/api/pdf/job/${job.jobCode}/${snapshot.stageName
          .toLowerCase()
          .replace(
            /\s+/g,
            '',
          )}/${snapshot.filePath}?companyId=${job.company.id}`;

    // --- COMPANY SNAPSHOT ---
    const companySnapshot = job.companySnapshot ?? {
      companyName: job.company.name,
      companyAddress: job.company.address,
      companyScope: job.company.scope_of_work,
      companycity: job.company.city,
      companycontact_person: job.company.contact_person,
      companydesignation: job.company.designation,
      companyemail: job.company.email,
      companymobile: job.company.mobile,
      companytelephone: job.company.telephone,
      companyFax: job.company.fax,
      companyvalidity: job.company.validity,
      companyaccreditation: job.company.accreditation,
      companycertification_body: job.company.accreditation,
    };

    return {
      company: { id: job.company.id, name: job.company.name },
      id: job.id,
      jobCodes: job.jobCode.split(',').map((code) => code.trim()),
      stage: job.stage,
      stages: [
        {
          stageName: job.stage,
          md: job.md,
          docReview: job.docReview,
          date: job.date,
          auditBy: job.leadAuditor,
        },
      ],
      template: {
        ...snapshot,
        url: pdfUrl,
      },
      companySnapshot, // <-- added snapshot in response
      standards: job.standards.map((s) => ({
        id: s.id,
        name: s.name,
        title: s.title,
      })),
      leadAuditor: plainToInstance(UserDto, job.leadAuditor, {
        excludeExtraneousValues: true,
      }),
      date: job.date,
      docReview: job.docReview,
      numEmployees: job.numEmployees,
      naceEacCodes: job.naceEacCodes,
      md: job.md,
      mdRisk: job.mdRisk,
      scheduleSlot: job.scheduleSlot,
      expectedSurveillanceDate: job.expectedSurveillanceDate,
      expectedIADate: job.expectedIADate,
      expectedMRMDate: job.expectedMRMDate,
      expectedInitialInquiryDate: job.expectedInitialInquiryDate,
      createdAt: job.createdAt,
      updatedAt: job.updatedAt,
    };
  }

  async remove(id: number) {
    const job = await this.jobRepo.findOne({ where: { id } });
    if (!job) throw new NotFoundException('Job not found');

    await this.jobRepo.remove(job);
    return { message: 'Job deleted successfully' };
  }

  async findByCompanyId(companyId: number): Promise<Job[]> {
    return this.jobRepository.find({
      where: { company: { id: companyId } },
      relations: [
        'company',
        'template',
        'template.contents',
        'leadAuditor',
        'standards',
      ],
    });
  }

  async findByJobCode(jobCode: string) {
    const job = await this.jobRepo.findOne({
      where: { jobCode },
      relations: [
        'company',
        'template',
        'template.contents',
        'standards',
        'auditStage',
        'leadAuditor',
      ],
    });

    if (!job) {
      throw new NotFoundException(`Job not found with code: ${jobCode}`);
    }

    return job;
  }
}
