import { Injectable, NotFoundException } from '@nestjs/common';
import { TenantConnectionService } from '../../database/tenant-connection.service';
import { requireCurrentTenant } from '../../database/tenant-context';
import { Member } from '../../database/tenant/entities/member.entity';
import { MemberFamily } from '../../database/tenant/entities/member-family.entity';
import { CreateMemberDto, UpdateMemberDto } from './dto/member.dto';

@Injectable()
export class MembersService {
  constructor(private readonly tenantConn: TenantConnectionService) {}

  private async ds() {
    const ctx = requireCurrentTenant();
    return this.tenantConn.getTenantDataSource(ctx.subdomain);
  }

  private async repo() {
    return (await this.ds()).getRepository(Member);
  }

  private async familyRepo() {
    return (await this.ds()).getRepository(MemberFamily);
  }

  // ── Auto-ID ───────────────────────────────────────────────
  private async nextMemberId(r: any): Promise<string> {
    const result = await r
      .createQueryBuilder('m')
      .select('COUNT(*)', 'cnt')
      .withDeleted()
      .getRawOne();
    const seq = String(Number(result?.cnt ?? 0) + 1).padStart(5, '0');
    return seq;
  }

  // ── List / Search (server-side paginated) ─────────────────
  async findAll(opts: {
    q?: string;
    status?: string;
    memberType?: string;
    officeId?: string;
    page?: number;
    limit?: number;
  }) {
    const { q, status, memberType, officeId, page = 1, limit = 50 } = opts;
    // Cap at max 200 to protect memory
    const safLimit = Math.min(Math.max(1, limit), 200);
    const safPage  = Math.max(1, page);

    const r = await this.repo();
    const qb = r
      .createQueryBuilder('m')
      // Only load list-view fields — full record loaded in getOne()
      .select([
        'm.id', 'm.memberId', 'm.name', 'm.nameEn', 'm.memberType',
        'm.contactNumber', 'm.citizenshipNumber', 'm.status', 'm.photoUrl',
        'm.officeId', 'm.pDistrict', 'm.pLocalgovt', 'm.createdAt', 'm.updatedAt',
      ])
      .leftJoin('m.office', 'office')
      .addSelect(['office.id', 'office.name'])
      .where('m.deletedAt IS NULL')
      .orderBy('m.createdAt', 'DESC')
      .skip((safPage - 1) * safLimit)
      .take(safLimit);

    if (q && q.trim()) {
      const like = `%${q.trim()}%`;
      qb.andWhere(
        `(m.name LIKE :q OR m.nameEn LIKE :q OR m.memberId LIKE :q
          OR m.contactNumber LIKE :q OR m.citizenshipNumber LIKE :q
          OR m.email LIKE :q)`,
        { q: like },
      );
    }
    if (status) qb.andWhere('m.status = :status', { status });
    if (memberType) qb.andWhere('m.memberType = :memberType', { memberType });
    if (officeId)   qb.andWhere('m.officeId = :officeId', { officeId });

    const [items, total] = await qb.getManyAndCount();
    return { items, total, page: safPage, limit: safLimit };
  }

  // ── Get one ───────────────────────────────────────────────
  async getOne(id: string) {
    const r = await this.repo();
    const m = await r.findOne({
      where: { id },
      relations: { office: true, family: true },
      order: { family: { order: 'ASC' } } as any,
    });
    if (!m) throw new NotFoundException('Member not found');
    return m;
  }

  // ── Create ────────────────────────────────────────────────
  async create(dto: CreateMemberDto, userId?: string) {
    const r = await this.repo();
    const { family, ...memberData } = dto;

    if (!memberData.memberId) {
      memberData.memberId = await this.nextMemberId(r);
    }

    const member = r.create({ ...memberData, createdBy: userId, updatedBy: userId });
    const saved = await r.save(member);

    if (family?.length) {
      const fr = await this.familyRepo();
      const rows = family.map((f, i) =>
        fr.create({ ...f, memberId: saved.id, order: f.order ?? i }),
      );
      await fr.save(rows);
      saved.family = rows;
    }

    return saved;
  }

  // ── Update ────────────────────────────────────────────────
  async update(id: string, dto: UpdateMemberDto, userId?: string) {
    const r = await this.repo();
    const m = await r.findOne({ where: { id } });
    if (!m) throw new NotFoundException('Member not found');

    const { family, ...memberData } = dto;
    r.merge(m, { ...memberData, updatedBy: userId });
    const saved = await r.save(m);

    if (family !== undefined) {
      const fr = await this.familyRepo();
      await fr.delete({ memberId: id });
      if (family.length) {
        const rows = family.map((f, i) =>
          fr.create({ ...f, memberId: id, order: f.order ?? i }),
        );
        await fr.save(rows);
      }
    }

    return saved;
  }

  // ── Remove ────────────────────────────────────────────────
  async remove(id: string) {
    const r = await this.repo();
    const m = await r.findOne({ where: { id } });
    if (!m) throw new NotFoundException('Member not found');
    await r.softDelete(id);
    return { success: true };
  }
}
