import { v4 as uuid } from 'uuid';
import {
  Op,
  QueryTypes,
  type CreationAttributes,
  type QueryInterface,
} from 'sequelize';
import {
  SEED_ORGANIZATION_ID,
  SEED_ORGANIZATION_NAME,
  SEED_ORGANIZATION_2_ID,
  SEED_ORGANIZATION_2_NAME,
  SEED_SECONDARY_OWNER,
  SEED_CAMPUS_ID,
  SEED_FIXTURE_USERS,
} from '@/shared/constants/seed-fixtures';
import { PRODUCT_CAMPUS_SEED_ROWS } from '@/shared/constants/campuses';
import type { Organizations } from '@/db/models/organizations';
import type { Staff } from '@/db/models/staff';

/**
 * RBAC fixture links (Workstream 4): one company that owns the seeded campuses,
 * the per-role users tied to the company/campus, and staff profiles covering
 * every campus staff role on the `tigers` campus. Runs after the user and role
 * seeders. Idempotent and reversible.
 */
const campusIds = PRODUCT_CAMPUS_SEED_ROWS.map((campus) => campus.id);
const staffFixtures = SEED_FIXTURE_USERS.filter((user) => user.staffType);
const staffUserIds = staffFixtures.map((user) => user.id);

export default {
  up: async (queryInterface: QueryInterface) => {
    const createdAt = new Date();
    const updatedAt = new Date();

    // 1. The companies (idempotent): the primary tenant and the second tenant
    //    used to prove cross-tenant isolation (Workstream 8).
    const orgSeeds: CreationAttributes<Organizations>[] = [
      { id: SEED_ORGANIZATION_ID, name: SEED_ORGANIZATION_NAME, createdAt, updatedAt },
      { id: SEED_ORGANIZATION_2_ID, name: SEED_ORGANIZATION_2_NAME, createdAt, updatedAt },
    ];
    const existingOrgs = await queryInterface.sequelize.query<{ id: string }>(
      `SELECT id FROM organizations WHERE id IN (:ids)`,
      {
        replacements: { ids: orgSeeds.map((org) => org.id) },
        type: QueryTypes.SELECT,
      },
    );
    const existingOrgIds = new Set(existingOrgs.map((row) => row.id));
    const orgsToInsert = orgSeeds.filter((org) => !existingOrgIds.has(org.id as string));
    if (orgsToInsert.length > 0) {
      await queryInterface.bulkInsert('organizations', orgsToInsert);
    }

    // The second-tenant owner is tied to the second organization.
    await queryInterface.sequelize.query(
      `UPDATE "users" SET "organizationId" = :org, "campusId" = NULL WHERE "id" = :id`,
      {
        replacements: {
          org: SEED_ORGANIZATION_2_ID,
          id: SEED_SECONDARY_OWNER.id,
        },
      },
    );

    // 2. The company owns the seeded campuses.
    await queryInterface.sequelize.query(
      `UPDATE "campuses" SET "organizationId" = :org WHERE "id" IN (:ids)`,
      { replacements: { org: SEED_ORGANIZATION_ID, ids: campusIds } },
    );

    // 3. Tie each fixture user to the company/campus per its scope.
    for (const user of SEED_FIXTURE_USERS) {
      await queryInterface.sequelize.query(
        `UPDATE "users" SET "organizationId" = :org, "campusId" = :campus WHERE "id" = :id`,
        {
          replacements: {
            org: user.organization ? SEED_ORGANIZATION_ID : null,
            campus: user.campus ? SEED_CAMPUS_ID : null,
            id: user.id,
          },
        },
      );
    }

    // 4. Staff profiles for the campus staff roles (idempotent by userId).
    const existingStaff = await queryInterface.sequelize.query<{
      userId: string;
    }>(`SELECT "userId" FROM staff WHERE "userId" IN (:ids)`, {
      replacements: { ids: staffUserIds },
      type: QueryTypes.SELECT,
    });
    const staffed = new Set(existingStaff.map((row) => row.userId));

    const staffRows: CreationAttributes<Staff>[] = staffFixtures
      .filter((user) => !staffed.has(user.id))
      .map((user) => ({
        id: uuid(),
        job_title: user.firstName,
        staff_type: user.staffType ?? null,
        status: 'active',
        organizationId: SEED_ORGANIZATION_ID,
        campusId: SEED_CAMPUS_ID,
        userId: user.id,
        createdAt,
        updatedAt,
      }));

    if (staffRows.length > 0) {
      await queryInterface.bulkInsert('staff', staffRows);
    }
  },

  down: async (queryInterface: QueryInterface) => {
    await queryInterface.bulkDelete(
      'staff',
      { userId: { [Op.in]: staffUserIds } },
      {},
    );
    await queryInterface.sequelize.query(
      `UPDATE "users" SET "organizationId" = NULL, "campusId" = NULL WHERE "id" IN (:ids)`,
      {
        replacements: {
          ids: [
            ...SEED_FIXTURE_USERS.map((user) => user.id),
            SEED_SECONDARY_OWNER.id,
          ],
        },
      },
    );
    await queryInterface.sequelize.query(
      `UPDATE "campuses" SET "organizationId" = NULL WHERE "id" IN (:ids)`,
      { replacements: { ids: campusIds } },
    );
    await queryInterface.bulkDelete(
      'organizations',
      { id: { [Op.in]: [SEED_ORGANIZATION_ID, SEED_ORGANIZATION_2_ID] } },
      {},
    );
  },
};
