import { MigrationInterface, QueryRunner } from "typeorm";

/**
 * Freight billing — `invoices` + `invoice_lines` tables.
 *
 * Matches:
 *   - billing/entities/invoice.entity.ts
 *   - billing/entities/invoice-line.entity.ts
 *
 * The status enum mirrors `Freight.InvoiceStatus` and uses TypeORM's default
 * enum-type name (`<table>_<column>_enum`) so the entity's `type: "enum"`
 * column resolves to it without an explicit `enumName`.
 */
export class CreateInvoices1821000000002 implements MigrationInterface {
  name = "CreateInvoices1821000000002";

  public async up(queryRunner: QueryRunner): Promise<void> {
    const typeExists = await queryRunner.query(
      `SELECT 1 FROM pg_type WHERE typname = 'invoices_status_enum' AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'freight');`,
    );

    if (!typeExists.length) {
      await queryRunner.query(`
        CREATE TYPE freight.invoices_status_enum AS ENUM (
          'DRAFT',
          'PENDING',
          'PAID',
          'OVERDUE',
          'CANCELLED',
          'REFUNDED'
        );
      `);
    }

    await queryRunner.query(`
      CREATE TABLE IF NOT EXISTS freight.invoices (
        id uuid NOT NULL DEFAULT uuid_generate_v4(),
        invoice_number varchar(64) NOT NULL,
        company_id uuid NOT NULL,
        company_profile_id uuid NOT NULL,
        total_amount numeric(14, 2) NOT NULL,
        currency varchar(8) NOT NULL DEFAULT 'ETB',
        status freight.invoices_status_enum NOT NULL DEFAULT 'DRAFT',
        source varchar(255) NOT NULL,
        source_id varchar(255) NOT NULL,
        type varchar(255) NOT NULL,
        issued_at timestamptz,
        payment_id uuid,
        due_at timestamptz NOT NULL,
        created_at timestamptz NOT NULL DEFAULT now(),
        updated_at timestamptz NOT NULL DEFAULT now(),
        deleted_at timestamptz,
        CONSTRAINT pk_invoices PRIMARY KEY (id),
        CONSTRAINT uq_invoices_invoice_number UNIQUE (invoice_number),
        CONSTRAINT fk_invoices_company FOREIGN KEY (company_id)
          REFERENCES freight.companies (id) ON DELETE RESTRICT,
        CONSTRAINT fk_invoices_company_profile FOREIGN KEY (company_profile_id)
          REFERENCES freight.company_profiles (id) ON DELETE RESTRICT,
        CONSTRAINT fk_invoices_payment FOREIGN KEY (payment_id)
          REFERENCES freight.payments (id) ON DELETE SET NULL
      );
    `);

    await queryRunner.query(
      `
      ALTER TABLE freight.invoices
        ADD COLUMN IF NOT EXISTS id uuid DEFAULT uuid_generate_v4(),
        ADD COLUMN IF NOT EXISTS invoice_number varchar(64),
        ADD COLUMN IF NOT EXISTS company_id uuid,
        ADD COLUMN IF NOT EXISTS company_profile_id uuid,
        ADD COLUMN IF NOT EXISTS total_amount numeric(14, 2) NOT NULL DEFAULT 0,
        ADD COLUMN IF NOT EXISTS currency varchar(8) NOT NULL DEFAULT 'ETB',
        ADD COLUMN IF NOT EXISTS status freight.invoices_status_enum NOT NULL DEFAULT 'DRAFT',
        ADD COLUMN IF NOT EXISTS source varchar(255),
        ADD COLUMN IF NOT EXISTS source_id varchar(255),
        ADD COLUMN IF NOT EXISTS type varchar(255),
        ADD COLUMN IF NOT EXISTS issued_at timestamptz,
        ADD COLUMN IF NOT EXISTS payment_id uuid,
        ADD COLUMN IF NOT EXISTS due_at timestamptz,
        ADD COLUMN IF NOT EXISTS created_at timestamptz NOT NULL DEFAULT now(),
        ADD COLUMN IF NOT EXISTS updated_at timestamptz NOT NULL DEFAULT now(),
        ADD COLUMN IF NOT EXISTS deleted_at timestamptz;
      `,
    );
    await queryRunner.query(`
      UPDATE freight.invoices
         SET due_at = COALESCE(due_at, issued_at, created_at, now())
       WHERE due_at IS NULL;
    `);
    await queryRunner.query(`ALTER TABLE freight.invoices ALTER COLUMN due_at SET NOT NULL;`);

    await queryRunner.query(`
      DO $$
      BEGIN
        IF NOT EXISTS (
          SELECT 1 FROM pg_constraint
           WHERE contype = 'p'
             AND conrelid = 'freight.invoices'::regclass
        ) THEN
          ALTER TABLE freight.invoices ADD CONSTRAINT pk_invoices PRIMARY KEY (id);
        END IF;

        IF NOT EXISTS (
          SELECT 1 FROM pg_constraint
           WHERE conname = 'uq_invoices_invoice_number'
             AND conrelid = 'freight.invoices'::regclass
        ) THEN
          ALTER TABLE freight.invoices ADD CONSTRAINT uq_invoices_invoice_number UNIQUE (invoice_number);
        END IF;

        IF NOT EXISTS (
          SELECT 1 FROM pg_constraint
           WHERE conname = 'fk_invoices_company'
             AND conrelid = 'freight.invoices'::regclass
        ) THEN
          ALTER TABLE freight.invoices ADD CONSTRAINT fk_invoices_company
            FOREIGN KEY (company_id) REFERENCES freight.companies (id) ON DELETE RESTRICT;
        END IF;

        IF NOT EXISTS (
          SELECT 1 FROM pg_constraint
           WHERE conname = 'fk_invoices_company_profile'
             AND conrelid = 'freight.invoices'::regclass
        ) THEN
          ALTER TABLE freight.invoices ADD CONSTRAINT fk_invoices_company_profile
            FOREIGN KEY (company_profile_id) REFERENCES freight.company_profiles (id) ON DELETE RESTRICT;
        END IF;

        IF NOT EXISTS (
          SELECT 1 FROM pg_constraint
           WHERE conname = 'fk_invoices_payment'
             AND conrelid = 'freight.invoices'::regclass
        ) THEN
          ALTER TABLE freight.invoices ADD CONSTRAINT fk_invoices_payment
            FOREIGN KEY (payment_id) REFERENCES freight.payments (id) ON DELETE SET NULL;
        END IF;
      END $$;
    `);

    await queryRunner.query(
      `CREATE INDEX IF NOT EXISTS idx_invoices_company ON freight.invoices (company_id);`,
    );
    await queryRunner.query(
      `CREATE INDEX IF NOT EXISTS idx_invoices_company_profile ON freight.invoices (company_profile_id);`,
    );
    await queryRunner.query(
      `CREATE INDEX IF NOT EXISTS idx_invoices_source ON freight.invoices (source, source_id);`,
    );
    await queryRunner.query(
      `CREATE INDEX IF NOT EXISTS idx_invoices_status ON freight.invoices (status);`,
    );

    await queryRunner.query(`
      CREATE TABLE IF NOT EXISTS freight.invoice_lines (
        id uuid NOT NULL DEFAULT uuid_generate_v4(),
        invoice_id uuid NOT NULL,
        charge_type varchar NOT NULL,
        description varchar(255),
        quantity numeric(12, 2) NOT NULL DEFAULT 1,
        unit_rate numeric(14, 2) NOT NULL DEFAULT 0,
        amount numeric(14, 2) NOT NULL,
        currency varchar(8) NOT NULL DEFAULT 'ETB',
        metadata jsonb,
        created_at timestamptz NOT NULL DEFAULT now(),
        updated_at timestamptz NOT NULL DEFAULT now(),
        deleted_at timestamptz,
        CONSTRAINT pk_invoice_lines PRIMARY KEY (id),
        CONSTRAINT fk_invoice_lines_invoice FOREIGN KEY (invoice_id)
          REFERENCES freight.invoices (id) ON DELETE CASCADE
      );
    `);

    await queryRunner.query(
      `CREATE INDEX IF NOT EXISTS idx_invoice_lines_invoice ON freight.invoice_lines (invoice_id);`,
    );
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`DROP TABLE IF EXISTS freight.invoice_lines;`);
    await queryRunner.query(`DROP TABLE IF EXISTS freight.invoices;`);
    await queryRunner.query(
      `DROP TYPE IF EXISTS freight.invoices_status_enum;`,
    );
  }
}
