import { Injectable, Logger } from "@nestjs/common";
import {
  Application,
  Organization,
  OrganizationConfiguration,
  Permission,
  Unit,
} from "@tria-plc/iamapi-common";
import { DataSource, EntityManager } from "typeorm";

import {
  EDR_FREIGHT_APPLICATION,
  EDR_FREIGHT_PERMISSIONS,
} from "./edr-freight.seed";

const EDR_UNIT_KEY = "edr_freight_app";
const EDR_UNIT_NAME = { en: "EDR Freight App" };

const EDR_ORG_KEY = "edr_freight";
const EDR_ORG_NAME = { en: "EDR Freight" };
const SEED_FLAG = "SEED_EDR_ORG";

type SeedOrganization = {
  id: string;
  key: string;
};

@Injectable()
export class EdrOrgSeeder {
  private readonly logger = new Logger(EdrOrgSeeder.name);

  constructor(private readonly dataSource: DataSource) {}

  async run() {
    if (!this.shouldSeed()) {
      this.logger.log(`Skipping EDR org seed because ${SEED_FLAG} is not enabled`);
      return;
    }

    await this.dataSource.transaction(async (manager) => {
      const organization = await this.ensureOrganization(manager);

      await this.ensureOrganizationConfiguration(manager, organization.id);
      await this.ensureDefaultUnit(manager, organization.id);

      const application = await this.ensureApplication(manager);
      await this.ensurePermissions(manager, application.id);

      // Roles, positions and their permission links are intentionally NOT
      // seeded for now — only the application-scoped permission catalog,
      // mirroring how the default IAM seed relates permissions to their
      // application. Grants are assigned later through the IAM UI.
    });

    this.logger.log(`Ensured EDR organization seed for '${EDR_ORG_KEY}'`);
  }

  private shouldSeed() {
    return process.env[SEED_FLAG]?.trim().toLowerCase() === "true";
  }

  private async ensureOrganization(
    manager: EntityManager,
  ): Promise<SeedOrganization> {
    const organizationRepository = manager.getRepository(Organization);
    let organization = await organizationRepository.findOne({
      where: { key: EDR_ORG_KEY },
      select: { id: true, key: true },
    });

    if (!organization) {
      const insertResult = await organizationRepository.insert({
        key: EDR_ORG_KEY,
        name: EDR_ORG_NAME,
        isGovernmentOrganization: true,
      });

      this.logger.log(`Seeded EDR organization '${EDR_ORG_KEY}'`);

      return {
        id: insertResult.identifiers[0]?.id as string,
        key: EDR_ORG_KEY,
      };
    }

    this.logger.log(`Ensured EDR organization '${EDR_ORG_KEY}'`);

    return {
      id: organization.id as string,
      key: EDR_ORG_KEY,
    };
  }

  private async ensureOrganizationConfiguration(
    manager: EntityManager,
    organizationId: string,
  ) {
    const organizationConfigurationRepository =
      manager.getRepository(OrganizationConfiguration);

    await organizationConfigurationRepository.upsert({
      organizationId,
      canCreateBranchByItself: true,
      canStartReceivingRecord: true,
    }, {
      conflictPaths: { organizationId: true },
    });

    this.logger.log(
      `Ensured organization configuration for '${EDR_ORG_KEY}'`,
    );
  }

  private async ensureDefaultUnit(
    manager: EntityManager,
    organizationId: string,
  ): Promise<{ id: string }> {
    const unitRepository = manager.getRepository(Unit);

    let unit = await unitRepository.findOne({
      where: { key: EDR_UNIT_KEY, organizationId },
      select: { id: true },
    });

    if (!unit) {
      const insertResult = await unitRepository.insert({
        key: EDR_UNIT_KEY,
        name: EDR_UNIT_NAME,
        organizationId,
      });
      this.logger.log(`Seeded EDR unit '${EDR_UNIT_KEY}'`);
      return { id: insertResult.identifiers[0]?.id as string };
    }

    this.logger.log(`Ensured EDR unit '${EDR_UNIT_KEY}'`);
    return { id: unit.id };
  }

  private async ensureApplication(
    manager: EntityManager,
  ): Promise<{ id: string }> {
    const applicationRepository = manager.getRepository(Application);

    const application = await applicationRepository.findOne({
      where: { key: EDR_FREIGHT_APPLICATION.key },
      select: { id: true },
    });

    if (!application?.id) {
      const insertResult = await applicationRepository.insert({
        id: EDR_FREIGHT_APPLICATION.id,
        key: EDR_FREIGHT_APPLICATION.key,
        name: { ...EDR_FREIGHT_APPLICATION.name },
      });
      this.logger.log(`Seeded EDR application '${EDR_FREIGHT_APPLICATION.key}'`);
      return { id: insertResult.identifiers[0]?.id as string };
    }

    this.logger.log(`Ensured EDR application '${EDR_FREIGHT_APPLICATION.key}'`);
    return { id: application.id };
  }

  private async ensurePermissions(
    manager: EntityManager,
    applicationId: string,
  ) {
    const permissionRepository = manager.getRepository(Permission);

    // Upsert by key so reruns are idempotent; applicationId ties every
    // permission to the EDR Freight application (also backfills rows that
    // were previously seeded without the relation).
    await permissionRepository.upsert(
      EDR_FREIGHT_PERMISSIONS.map((permission) => ({
        id: permission.id,
        key: permission.key,
        name: { ...permission.name },
        applicationId,
      })),
      { conflictPaths: { key: true } },
    );

    this.logger.log(
      `Ensured ${EDR_FREIGHT_PERMISSIONS.length} permissions on application '${EDR_FREIGHT_APPLICATION.key}'`,
    );
  }
}
