import { v4 as uuid } from 'uuid';
import { QueryTypes, type QueryInterface } from 'sequelize';
import { ROLE_NAMES } from '@/shared/constants/roles';
import {
  SEED_ORGANIZATION_ID,
  SEED_CAMPUS_ID,
  SEED_CLASS_ID,
  SEED_CLASS_NAME,
  SEED_FIXTURE_USERS,
  SEED_ORGANIZATION_2_ID,
  SEED_SECONDARY_CAMPUS_ID,
  SEED_SECONDARY_CLASS_ID,
  SEED_SECONDARY_CLASS_NAME,
  SEED_SECONDARY_USERS,
} from '@/shared/constants/seed-fixtures';

/**
 * Class-tier fixtures: one homeroom per seeded tenant, with teacher/
 * support_staff users assigned to it, the student enrolled
 * (`class_enrollments`), and the guardian linked to that student
 * (`guardian_students`). Runs after the rbac-fixtures seeder. Idempotent.
 */
function fixtureId(
  users: typeof SEED_FIXTURE_USERS | typeof SEED_SECONDARY_USERS,
  role: string,
): string | null {
  return users.find((u) => u.role === role)?.id ?? null;
}

const classFixtures = [
  {
    organizationId: SEED_ORGANIZATION_ID,
    campusId: SEED_CAMPUS_ID,
    classId: SEED_CLASS_ID,
    className: SEED_CLASS_NAME,
    teacherId: fixtureId(SEED_FIXTURE_USERS, ROLE_NAMES.TEACHER),
    supportId: fixtureId(SEED_FIXTURE_USERS, ROLE_NAMES.SUPPORT_STAFF),
    studentId: fixtureId(SEED_FIXTURE_USERS, ROLE_NAMES.STUDENT),
    guardianId: fixtureId(SEED_FIXTURE_USERS, ROLE_NAMES.GUARDIAN),
  },
  {
    organizationId: SEED_ORGANIZATION_2_ID,
    campusId: SEED_SECONDARY_CAMPUS_ID,
    classId: SEED_SECONDARY_CLASS_ID,
    className: SEED_SECONDARY_CLASS_NAME,
    teacherId: fixtureId(SEED_SECONDARY_USERS, ROLE_NAMES.TEACHER),
    supportId: fixtureId(SEED_SECONDARY_USERS, ROLE_NAMES.SUPPORT_STAFF),
    studentId: fixtureId(SEED_SECONDARY_USERS, ROLE_NAMES.STUDENT),
    guardianId: fixtureId(SEED_SECONDARY_USERS, ROLE_NAMES.GUARDIAN),
  },
] as const;

function classMemberUserIds(
  fixture: (typeof classFixtures)[number],
): readonly string[] {
  return [fixture.teacherId, fixture.supportId].filter(
    (id): id is string => id != null,
  );
}

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

    for (const fixture of classFixtures) {
      // 1. The homeroom class (idempotent), with the teacher as homeroom teacher.
      const existingClass = await queryInterface.sequelize.query<{ id: string }>(
        `SELECT id FROM classes WHERE id = :id`,
        { replacements: { id: fixture.classId }, type: QueryTypes.SELECT },
      );
      if (existingClass.length === 0) {
        await queryInterface.bulkInsert('classes', [
          {
            id: fixture.classId,
            name: fixture.className,
            campusId: fixture.campusId,
            organizationId: fixture.organizationId,
            homeroom_teacherId: fixture.teacherId ?? null,
            createdAt,
            updatedAt,
          },
        ]);
      }

      // 2. Assign teacher/support to the class.
      const memberUserIds = classMemberUserIds(fixture);
      if (memberUserIds.length > 0) {
        await queryInterface.sequelize.query(
          `UPDATE "users" SET "classId" = :cls WHERE "id" IN (:ids)`,
          { replacements: { cls: fixture.classId, ids: memberUserIds } },
        );
      }

      // 3. Enroll the student in the class (idempotent).
      if (fixture.studentId) {
        const existingEnrollment = await queryInterface.sequelize.query<{
          id: string;
        }>(
          `SELECT id FROM class_enrollments WHERE "classId" = :cls AND "studentId" = :sid`,
          {
            replacements: { cls: fixture.classId, sid: fixture.studentId },
            type: QueryTypes.SELECT,
          },
        );
        if (existingEnrollment.length === 0) {
          await queryInterface.bulkInsert('class_enrollments', [
            {
              id: uuid(),
              classId: fixture.classId,
              studentId: fixture.studentId,
              organizationId: fixture.organizationId,
              enrolled_on: createdAt,
              createdAt,
              updatedAt,
            },
          ]);
        }
      }

      // 4. Link the guardian to the student (idempotent).
      if (fixture.guardianId && fixture.studentId) {
        const existingLink = await queryInterface.sequelize.query<{ id: string }>(
          `SELECT id FROM guardian_students WHERE "guardianId" = :gid AND "studentId" = :sid`,
          {
            replacements: { gid: fixture.guardianId, sid: fixture.studentId },
            type: QueryTypes.SELECT,
          },
        );
        if (existingLink.length === 0) {
          await queryInterface.bulkInsert('guardian_students', [
            {
              id: uuid(),
              guardianId: fixture.guardianId,
              studentId: fixture.studentId,
              relationship: 'parent',
              organizationId: fixture.organizationId,
              createdAt,
              updatedAt,
            },
          ]);
        }
      }
    }
  },

  down: async (queryInterface: QueryInterface) => {
    for (const fixture of classFixtures) {
      if (fixture.studentId) {
        await queryInterface.sequelize.query(
          `DELETE FROM guardian_students WHERE "studentId" = :sid`,
          { replacements: { sid: fixture.studentId } },
        );
        await queryInterface.sequelize.query(
          `DELETE FROM class_enrollments WHERE "classId" = :cls AND "studentId" = :sid`,
          { replacements: { cls: fixture.classId, sid: fixture.studentId } },
        );
      }
      const memberUserIds = classMemberUserIds(fixture);
      if (memberUserIds.length > 0) {
        await queryInterface.sequelize.query(
          `UPDATE "users" SET "classId" = NULL WHERE "id" IN (:ids)`,
          { replacements: { ids: memberUserIds } },
        );
      }
      await queryInterface.sequelize.query(`DELETE FROM classes WHERE id = :id`, {
        replacements: { id: fixture.classId },
      });
    }
  },
};
