import { Injectable, NotFoundException } from '@nestjs/common';
import { TenantConnectionService } from '../../database/tenant-connection.service';
import { Center } from '../../database/tenant/entities/center.entity';
import { Group } from '../../database/tenant/entities/group.entity';
import { StaffPosition } from '../../database/tenant/entities/staff-position.entity';
import { Insurance } from '../../database/tenant/entities/insurance.entity';
import { DocumentType } from '../../database/tenant/entities/document-type.entity';
import { Valuator } from '../../database/tenant/entities/valuator.entity';
import { NewspaperPublish } from '../../database/tenant/entities/newspaper-publish.entity';
import { BusinessUnit } from '../../database/tenant/entities/business-unit.entity';
import { Department } from '../../database/tenant/entities/department.entity';
import { CommitteeGroup } from '../../database/tenant/entities/committee-group.entity';
import { CreditAnalysisTitle } from '../../database/tenant/entities/credit-analysis-title.entity';
import { CreditAnalysisOption } from '../../database/tenant/entities/credit-analysis-option.entity';
import { REQUEST } from '@nestjs/core';
import { Inject } from '@nestjs/common';
import { Request } from 'express';

@Injectable()
export class ExtendedMasterService {
  constructor(
    private tenantConn: TenantConnectionService,
    @Inject(REQUEST) private request: Request,
  ) {}

  private sub(): string { return (this.request as any).tenantSubdomain as string; }
  private async ds() { return this.tenantConn.getTenantDataSource(this.sub()); }

  private async repo<T>(Entity: new () => T) { return (await this.ds()).getRepository(Entity); }

  // ── Centers ────────────────────────────────────────────────────────────────
  async listCenters(q?: string, officeId?: string) {
    const r = await this.repo(Center);
    const qb = r.createQueryBuilder('c').where('c.deletedAt IS NULL').orderBy('c.name');
    if (q) qb.andWhere('(c.name LIKE :q OR c.address LIKE :q)', { q: `%${q}%` });
    if (officeId) qb.andWhere('c.officeId = :officeId', { officeId });
    return qb.getMany();
  }
  async createCenter(dto: Partial<Center>) {
    const r = await this.repo(Center);
    return r.save(r.create({ ...dto, active: dto.active ?? true }));
  }
  async updateCenter(id: string, dto: Partial<Center>) {
    const r = await this.repo(Center);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException('Center not found');
    return r.save(Object.assign(e, dto));
  }
  async deleteCenter(id: string) {
    const r = await this.repo(Center);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    await r.softDelete(id);
    return { id };
  }

  // ── Groups ─────────────────────────────────────────────────────────────────
  async listGroups(q?: string, officeId?: string, centerId?: string) {
    const r = await this.repo(Group);
    const qb = r.createQueryBuilder('g').where('g.deletedAt IS NULL').orderBy('g.name');
    if (q) qb.andWhere('g.name LIKE :q', { q: `%${q}%` });
    if (officeId) qb.andWhere('g.officeId = :officeId', { officeId });
    if (centerId) qb.andWhere('g.centerId = :centerId', { centerId });
    return qb.getMany();
  }
  async createGroup(dto: Partial<Group>) {
    const r = await this.repo(Group);
    return r.save(r.create({ ...dto, active: dto.active ?? true }));
  }
  async updateGroup(id: string, dto: Partial<Group>) {
    const r = await this.repo(Group);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException('Group not found');
    return r.save(Object.assign(e, dto));
  }
  async deleteGroup(id: string) {
    const r = await this.repo(Group);
    await r.softDelete(id);
    return { id };
  }

  // ── Staff Positions ────────────────────────────────────────────────────────
  async listStaffPositions(q?: string, officeId?: string) {
    const r = await this.repo(StaffPosition);
    const qb = r.createQueryBuilder('sp').where('sp.deletedAt IS NULL').orderBy('sp.name');
    if (q) qb.andWhere('sp.name LIKE :q', { q: `%${q}%` });
    if (officeId) qb.andWhere('sp.officeId = :officeId', { officeId });
    return qb.getMany();
  }
  async createStaffPosition(dto: Partial<StaffPosition>) {
    const r = await this.repo(StaffPosition);
    return r.save(r.create({ ...dto, active: dto.active ?? true }));
  }
  async updateStaffPosition(id: string, dto: Partial<StaffPosition>) {
    const r = await this.repo(StaffPosition);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    return r.save(Object.assign(e, dto));
  }
  async deleteStaffPosition(id: string) {
    const r = await this.repo(StaffPosition);
    await r.softDelete(id);
    return { id };
  }

  // ── Insurance ──────────────────────────────────────────────────────────────
  async listInsurance(q?: string) {
    const r = await this.repo(Insurance);
    const qb = r.createQueryBuilder('i').where('i.deletedAt IS NULL').orderBy('i.name');
    if (q) qb.andWhere('(i.name LIKE :q OR i.address LIKE :q)', { q: `%${q}%` });
    return qb.getMany();
  }
  async createInsurance(dto: Partial<Insurance>) {
    const r = await this.repo(Insurance);
    return r.save(r.create({ ...dto, active: dto.active ?? true }));
  }
  async updateInsurance(id: string, dto: Partial<Insurance>) {
    const r = await this.repo(Insurance);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    return r.save(Object.assign(e, dto));
  }
  async deleteInsurance(id: string) {
    const r = await this.repo(Insurance);
    await r.softDelete(id);
    return { id };
  }

  // ── Document Types ─────────────────────────────────────────────────────────
  async listDocumentTypes(q?: string, type?: string) {
    const r = await this.repo(DocumentType);
    const qb = r.createQueryBuilder('dt').where('dt.deletedAt IS NULL').orderBy('dt.name');
    if (q) qb.andWhere('dt.name LIKE :q', { q: `%${q}%` });
    if (type) qb.andWhere('dt.type = :type', { type });
    return qb.getMany();
  }
  async createDocumentType(dto: Partial<DocumentType>) {
    const r = await this.repo(DocumentType);
    return r.save(r.create({ ...dto, active: dto.active ?? true }));
  }
  async updateDocumentType(id: string, dto: Partial<DocumentType>) {
    const r = await this.repo(DocumentType);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    return r.save(Object.assign(e, dto));
  }
  async deleteDocumentType(id: string) {
    const r = await this.repo(DocumentType);
    await r.softDelete(id);
    return { id };
  }

  // ── Valuators ──────────────────────────────────────────────────────────────
  async listValuators(q?: string) {
    const r = await this.repo(Valuator);
    const qb = r.createQueryBuilder('v').where('v.deletedAt IS NULL').orderBy('v.name');
    if (q) qb.andWhere('(v.name LIKE :q OR v.address LIKE :q)', { q: `%${q}%` });
    return qb.getMany();
  }
  async createValuator(dto: Partial<Valuator>) {
    const r = await this.repo(Valuator);
    return r.save(r.create({ ...dto, active: dto.active ?? true }));
  }
  async updateValuator(id: string, dto: Partial<Valuator>) {
    const r = await this.repo(Valuator);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    return r.save(Object.assign(e, dto));
  }
  async deleteValuator(id: string) {
    const r = await this.repo(Valuator);
    await r.softDelete(id);
    return { id };
  }

  // ── Newspaper Publish ──────────────────────────────────────────────────────
  async listNewspapers(q?: string) {
    const r = await this.repo(NewspaperPublish);
    const qb = r.createQueryBuilder('n').where('n.deletedAt IS NULL').orderBy('n.name');
    if (q) qb.andWhere('n.name LIKE :q', { q: `%${q}%` });
    return qb.getMany();
  }
  async createNewspaper(dto: Partial<NewspaperPublish>) {
    const r = await this.repo(NewspaperPublish);
    return r.save(r.create({ ...dto, active: dto.active ?? true }));
  }
  async updateNewspaper(id: string, dto: Partial<NewspaperPublish>) {
    const r = await this.repo(NewspaperPublish);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    return r.save(Object.assign(e, dto));
  }
  async deleteNewspaper(id: string) {
    const r = await this.repo(NewspaperPublish);
    await r.softDelete(id);
    return { id };
  }

  // ── Business Units ─────────────────────────────────────────────────────────
  async listBusinessUnits(q?: string) {
    const r = await this.repo(BusinessUnit);
    const qb = r.createQueryBuilder('bu').where('bu.deletedAt IS NULL').orderBy('bu.name');
    if (q) qb.andWhere('bu.name LIKE :q', { q: `%${q}%` });
    return qb.getMany();
  }
  async createBusinessUnit(dto: Partial<BusinessUnit>) {
    const r = await this.repo(BusinessUnit);
    return r.save(r.create({ ...dto, active: dto.active ?? true }));
  }
  async updateBusinessUnit(id: string, dto: Partial<BusinessUnit>) {
    const r = await this.repo(BusinessUnit);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    return r.save(Object.assign(e, dto));
  }
  async deleteBusinessUnit(id: string) {
    const r = await this.repo(BusinessUnit);
    await r.softDelete(id);
    return { id };
  }

  // ── Departments ────────────────────────────────────────────────────────────
  async listDepartments(q?: string) {
    const r = await this.repo(Department);
    const qb = r.createQueryBuilder('d').where('d.deletedAt IS NULL').orderBy('d.name');
    if (q) qb.andWhere('d.name LIKE :q', { q: `%${q}%` });
    return qb.getMany();
  }
  async createDepartment(dto: Partial<Department>) {
    const r = await this.repo(Department);
    return r.save(r.create({ ...dto, active: dto.active ?? true }));
  }
  async updateDepartment(id: string, dto: Partial<Department>) {
    const r = await this.repo(Department);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    return r.save(Object.assign(e, dto));
  }
  async deleteDepartment(id: string) {
    const r = await this.repo(Department);
    await r.softDelete(id);
    return { id };
  }

  // ── Committee Groups ───────────────────────────────────────────────────────
  async listCommitteeGroups(q?: string, officeId?: string) {
    const r = await this.repo(CommitteeGroup);
    const qb = r.createQueryBuilder('cg').where('cg.deletedAt IS NULL').orderBy('cg.name');
    if (q) qb.andWhere('cg.name LIKE :q', { q: `%${q}%` });
    if (officeId) qb.andWhere('cg.officeId = :officeId', { officeId });
    return qb.getMany();
  }
  async createCommitteeGroup(dto: Partial<CommitteeGroup>) {
    const r = await this.repo(CommitteeGroup);
    return r.save(r.create({ ...dto, active: dto.active ?? true }));
  }
  async updateCommitteeGroup(id: string, dto: Partial<CommitteeGroup>) {
    const r = await this.repo(CommitteeGroup);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    return r.save(Object.assign(e, dto));
  }
  async deleteCommitteeGroup(id: string) {
    const r = await this.repo(CommitteeGroup);
    await r.softDelete(id);
    return { id };
  }

  // ── Credit Analysis Titles ─────────────────────────────────────────────────
  async listCreditAnalysisTitles(officeId?: string) {
    const r = await this.repo(CreditAnalysisTitle);
    const qb = r.createQueryBuilder('cat').where('cat.deletedAt IS NULL').orderBy('cat.title');
    if (officeId) qb.andWhere('cat.officeId = :officeId', { officeId });
    const titles = await qb.getMany();
    const optRepo = await this.repo(CreditAnalysisOption);
    const result = [];
    for (const t of titles) {
      const options = await optRepo.find({ where: { titleId: t.id } });
      result.push({ ...t, options });
    }
    return result;
  }
  async createCreditAnalysisTitle(dto: any) {
    const r = await this.repo(CreditAnalysisTitle);
    return r.save(r.create({ ...dto }));
  }
  async updateCreditAnalysisTitle(id: string, dto: any) {
    const r = await this.repo(CreditAnalysisTitle);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    return r.save(Object.assign(e, dto));
  }
  async deleteCreditAnalysisTitle(id: string) {
    const r = await this.repo(CreditAnalysisTitle);
    await r.softDelete(id);
    return { id };
  }
  async createCreditAnalysisOption(titleId: string, dto: any) {
    const r = await this.repo(CreditAnalysisOption);
    return r.save(r.create({ ...dto, titleId }));
  }
  async updateCreditAnalysisOption(id: string, dto: any) {
    const r = await this.repo(CreditAnalysisOption);
    const e = await r.findOne({ where: { id } });
    if (!e) throw new NotFoundException();
    return r.save(Object.assign(e, dto));
  }
  async deleteCreditAnalysisOption(id: string) {
    const r = await this.repo(CreditAnalysisOption);
    await r.remove(await r.findOne({ where: { id } }) as any);
    return { id };
  }
}
