import 'dotenv/config';
import { PrismaPg } from '@prisma/adapter-pg';
import { Prisma, PrismaClient } from '../src/generated/prisma/client';
import {
  normalizeSeedName,
  validateIssueMatrixSeedTarget,
} from './seeds/issue-matrix.seed';
import { loadRgcDecisionSeedSource } from './seeds/rgc-decisions.seed';

// Reuse the reviewed historical date/ministry references, not the RGC importer.
// This command creates a plenary and ministry links only. It is NOT in the main seed.
export async function seedHistoricalPlenary(
  prisma: PrismaClient,
  apply = false,
) {
  const source = loadRgcDecisionSeedSource();
  const name = 'វេទិកាទី១៩';
  if (source.plenary.meetingDate !== '2023-11-13') {
    throw new Error(
      'The reviewed historical source must be dated 13 November 2023.',
    );
  }
  const meetingDate = new Date('2023-11-13T00:00:00.000Z');
  const nextDay = new Date('2023-11-14T00:00:00.000Z');
  const names = [
    name,
    source.plenary.name,
    ...source.plenary.matchingNames,
  ].map(normalizeSeedName);
  const ministryNames = [
    ...new Set(source.decisions.map((row) => row.ministry)),
  ];

  return prisma.$transaction(
    async (tx) => {
      if (apply) {
        await tx.$queryRaw`SELECT 1 AS locked FROM pg_advisory_xact_lock(20261030, 33)`;
        await tx.$executeRaw`LOCK TABLE "plenaries", "plenary_ministries" IN SHARE ROW EXCLUSIVE MODE`;
      } else {
        await tx.$executeRaw`SET TRANSACTION READ ONLY`;
      }
      // Include deleted matches: never silently recreate or restore a deleted plenary.
      const sameDate = await tx.plenary.findMany({
        where: { meetingDate: { gte: meetingDate, lt: nextDay } },
        select: { id: true, name: true, status: true, deletedAt: true },
      });
      const matches = sameDate.filter((row) =>
        names.includes(normalizeSeedName(row.name)),
      );
      if (matches.length > 1 || (sameDate.length > 0 && matches.length === 0)) {
        throw new Error(
          'Ambiguous existing plenary on 13 November 2023; nothing was changed.',
        );
      }
      if (matches[0]) {
        return {
          action: 'kept',
          record: matches[0],
          message: matches[0].deletedAt
            ? 'Deleted historical plenary preserved; restore it explicitly through Recycle Bin.'
            : 'Existing historical plenary preserved; no fields or ministry links were changed.',
        };
      }

      const owners = await tx.user.findMany({
        where: {
          email: 'cdcgpsf@gmail.com',
          isActive: true,
          deletedAt: null,
          roles: {
            some: {
              role: { name: 'cdc_g-psf', guardName: 'web', deletedAt: null },
            },
          },
        },
        select: { id: true },
      });
      if (owners.length !== 1)
        throw new Error(
          'Expected exactly one active CDC G-PSF account: cdcgpsf@gmail.com.',
        );
      const ministries = await tx.stakeholder.findMany({
        where: {
          name: { in: ministryNames },
          active: true,
          deletedAt: null,
          stakeholderType: { name: 'Ministry', deletedAt: null },
        },
        select: { id: true, name: true },
        orderBy: { id: 'asc' },
      });
      if (
        ministryNames.length === 0 ||
        ministryNames.some(
          (name) => ministries.filter((row) => row.name === name).length !== 1,
        )
      ) {
        throw new Error(
          'Every historical ministry must match exactly one active Ministry stakeholder.',
        );
      }
      const planned = {
        name,
        meetingDate,
        status: 'DRAFT' as const,
        userId: owners[0].id,
        deadline: null,
        documentReference: null,
      };
      if (!apply)
        return { action: 'would-create', record: planned, ministries };
      const created = await tx.plenary.create({
        data: {
          ...planned,
          ministries: {
            create: ministries.map((row) => ({ stakeholderId: row.id })),
          },
        },
        select: {
          id: true,
          name: true,
          meetingDate: true,
          status: true,
          userId: true,
        },
      });
      return { action: 'created', record: created, ministries };
    },
    {
      isolationLevel: Prisma.TransactionIsolationLevel.Serializable,
      timeout: 30000,
    },
  );
}

async function main() {
  const args = process.argv.slice(2);
  const databaseArgs = args.filter((arg) => arg.startsWith('--database='));
  if (
    databaseArgs.length > 1 ||
    new Set(args).size !== args.length ||
    args.some((arg) => arg !== '--apply' && !arg.startsWith('--database='))
  ) {
    throw new Error(
      'Use --apply and optionally --database=NAME. Omit --apply to preview.',
    );
  }
  const connectionString = process.env.DATABASE_URL;
  if (!connectionString) throw new Error('DATABASE_URL is missing.');
  const target = validateIssueMatrixSeedTarget(
    connectionString,
    databaseArgs[0]?.slice(11),
  );
  const prisma = new PrismaClient({
    adapter: new PrismaPg({ connectionString }),
  });
  try {
    console.log(
      JSON.stringify(
        {
          target,
          ...(await seedHistoricalPlenary(prisma, args.includes('--apply'))),
        },
        null,
        2,
      ),
    );
  } finally {
    await prisma.$disconnect();
  }
}

if (require.main === module) {
  main().catch((error: Error) => {
    console.error(error.message);
    process.exitCode = 1;
  });
}
