import { Injectable, InternalServerErrorException } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { BaseRepository } from "@edr/api-common";
import { CompanyProfile, ProfileStatus, ProfileType } from "./entities/company-profile.entity";

const SEQUENCE_MAP: Record<ProfileType, string> = {
  [ProfileType.exporter]: "seq_company_profile_ex",
  [ProfileType.importer]: "seq_company_profile_im",
  [ProfileType.freightForwarder]: "seq_company_profile_ffe",
  [ProfileType.djFreightForwarder]: "seq_company_profile_fwj",
  [ProfileType.transporter]: "seq_company_profile_tr",
};

const PREFIX_MAP: Record<ProfileType, string> = {
  [ProfileType.exporter]: "EX",
  [ProfileType.importer]: "IM",
  [ProfileType.freightForwarder]: "FF",
  [ProfileType.djFreightForwarder]: "FWJ",
  [ProfileType.transporter]: "TR",
};

const SERIES_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

/** Numbers per series letter: A00001..A99999, then B00001. */
const SERIES_SIZE = 99_999;

@Injectable()
export class CompanyProfileRepository extends BaseRepository<CompanyProfile> {
  constructor(
    @InjectRepository(CompanyProfile)
    repo: Repository<CompanyProfile>,
  ) {
    super(repo);
  }

  async generateReference(type: ProfileType): Promise<string> {
    // The sequences live in the same schema as the entity (e.g. "freight"), but
    // the connection's search_path is "public" — so the sequence MUST be
    // schema-qualified or `nextval` fails with "relation does not exist".
    const schema = this.repository.metadata.schema ?? "public";
    const seqName = `"${schema}".${SEQUENCE_MAP[type]}`;
    const result = await this.repository.query(
      `SELECT nextval('${seqName}') AS next_id`,
    );
    const nextId = Number(result[0].next_id);
    const offset = nextId - 1;
    const seriesIndex = Math.floor(offset / SERIES_SIZE);

    if (seriesIndex >= SERIES_LETTERS.length) {
      throw new InternalServerErrorException(
        `Company profile reference series exhausted for type "${type}"`,
      );
    }

    const letter = SERIES_LETTERS[seriesIndex];
    const number = (offset % SERIES_SIZE) + 1;
    const prefix = PREFIX_MAP[type];
    return `${prefix}-${letter}${String(number).padStart(5, "0")}`;
  }

  async findByCompanyId(companyId: string): Promise<CompanyProfile[]> {
    return this.repository.find({
      where: { companyId },
      relations: ["company"],
    });
  }

  async findByType(
    companyId: string,
    type: ProfileType,
  ): Promise<CompanyProfile | null> {
    return this.repository.findOne({
      where: { companyId, type },
    });
  }

  async findByReference(reference: string): Promise<CompanyProfile | null> {
    return this.repository.findOne({ where: { reference } });
  }

  async findById(id: string): Promise<CompanyProfile | null> {
    return this.repository.findOne({ where: { id } });
  }

  async updateStatus(
    id: string,
    status: ProfileStatus,
  ): Promise<CompanyProfile | null> {
    await this.repository.update({ id }, { status });
    return this.repository.findOne({ where: { id } });
  }
}
