import type { PrismaClient } from '../../src/generated/prisma/client';
import { GUARD, SUPER_ADMIN_ROLE, type SeedRoleMap } from './role.seed';

export const SUPER_ADMIN_PERMISSION = 'manage all';

// Permissions shown in the role create/edit matrix.
const PERMISSION_ACTIONS = ['read', 'create', 'update', 'delete'] as const;

const PERMISSION_SUBJECTS = [
  'User',
  'Role',
  'Permission',
  'Stakeholder',
  'WorkingGroupIssue',
  'IssueMatrix',
  'MeetingRequest',
  'Meeting',
  'MeetingSummary',
  'MeetingSummaryStatus',
  'ProgressReport',
  'ProgressReportMinistry',
  'CdcIssueMatrix',
  'CefpIssueMatrix',
] as const;

// Extra permissions that only apply to specific subjects (not full CRUD).
const EXTRA_PERMISSIONS = [
  'export WorkingGroupIssue',
  'export IssueMatrix',
  'export MeetingSummary',
  'export ProgressReportMinistry',
  // The dashboard is read-only, so it only needs a single "read" permission.
  // Adding it as a full CRUD subject would create unusable create/update/
  // delete rows.
  'read Dashboard',
];

const DEFAULT_PERMISSIONS = [
  ...PERMISSION_SUBJECTS.flatMap((subject) =>
    PERMISSION_ACTIONS.map((action) => `${action} ${subject}`),
  ),
  ...EXTRA_PERMISSIONS,
];

export const ROLE_PERMISSION_ASSIGNMENTS: Record<string, string[]> = {
  private_sector: [
    'read WorkingGroupIssue',
    'create WorkingGroupIssue',
    'export WorkingGroupIssue',
    'update WorkingGroupIssue',
    'read IssueMatrix',
    'export IssueMatrix',
    // Private sector creates and manages its own meeting requests, but cannot
    // delete them.
    'read MeetingRequest',
    'create MeetingRequest',
    'update MeetingRequest',
    'read Meeting',
    'read MeetingSummary',
    // Lets PSWG mark a shared summary as reviewed (status change). The
    // /meeting-summaries/:id/status endpoint row-scopes this to summaries
    // shared with the user's working group.
    'update MeetingSummary',
    // Read the status list (e.g. to populate a status dropdown).
    'read MeetingSummaryStatus',
    // PSWG can read Ministry reports shared with it and mark them reviewed.
    'read ProgressReport',
    'read ProgressReportMinistry',
    'update ProgressReportMinistry',
    // View the summary dashboard (cards + charts).
    'read Dashboard',
  ],
  ministry: [
    // Ministry reads the meeting requests addressed to it (row-level scoping is
    // applied in the service).
    'read MeetingRequest',
    // Ministry starts the review workflow for requests addressed to it.
    'update MeetingRequest',
    'read Meeting',
    'create Meeting',
    'update Meeting',
    'delete Meeting',
    'read MeetingSummary',
    'create MeetingSummary',
    'update MeetingSummary',
    'delete MeetingSummary',
    'read MeetingSummaryStatus',
    'read WorkingGroupIssue',
    'create WorkingGroupIssue',
    'export WorkingGroupIssue',
    'update WorkingGroupIssue',
    'read IssueMatrix',
    // Ministry users can view progress reports but cannot change them.
    'read ProgressReport',
    'read ProgressReportMinistry',
    'update ProgressReportMinistry',
    // View the summary dashboard (cards + charts).
    'read Dashboard',
  ],
  cdc: [
    'read CdcIssueMatrix',
    'create CdcIssueMatrix',
    // CDC Secretariat flags matrix issues for the plenary (and un-flags them).
    'update CdcIssueMatrix',
    'delete CdcIssueMatrix',
    // The shared issue matrix (/cdc/issue-matrix and /cefp/issue-matrix) is a
    // different subject: reading it shows the plenary escalation column, and
    // updating it drives that checkbox and the status/agency row edits.
    'read IssueMatrix',
    'update IssueMatrix',
    'export IssueMatrix',
    // The shared issue form lookups are served by WorkingGroupIssuesModule.
    'read WorkingGroupIssue',
    'read ProgressReport',
    'read Dashboard',
  ],
  // CEFP works the same way as CDC on its own matrix: it raises issues for a
  // working group and manages them, and sees only its own escalations.
  cefp: [
    'read CefpIssueMatrix',
    'create CefpIssueMatrix',
    'update CefpIssueMatrix',
    'delete CefpIssueMatrix',
    // The shared issue matrix (/cdc/issue-matrix and /cefp/issue-matrix) is a
    // different subject: reading it shows the plenary escalation column, and
    // updating it drives that checkbox and the status/agency row edits.
    'read IssueMatrix',
    'update IssueMatrix',
    'export IssueMatrix',
    // The shared issue form lookups are served by WorkingGroupIssuesModule.
    'read WorkingGroupIssue',
    'read ProgressReport',
    'read Dashboard',
  ],
  'cdc_g-psf': [
    'read CdcIssueMatrix',
    'read WorkingGroupIssue',
    'read IssueMatrix',
    'read Meeting',
    'read MeetingRequest',
    'read MeetingSummary',
    // CDC G-PSF manages the complete progress-report workflow.
    'read ProgressReport',
    'create ProgressReport',
    'update ProgressReport',
    'delete ProgressReport',
    'read ProgressReportMinistry',
    'update ProgressReportMinistry',
    // Lets CDC G-PSF download the meeting summary table as an Excel file.
    'export MeetingSummary',
    // Lets CDC G-PSF download the issue matrix table as an Excel file.
    'export IssueMatrix',
    // Lets CDC G-PSF download the comments on a progress report.
    'export ProgressReportMinistry',
    // View the summary dashboard (cards + charts).
    'read Dashboard',
  ],
};

export async function seedPermissions(
  prisma: PrismaClient,
  rolesByName: SeedRoleMap,
) {
  let permission = await prisma.permissions.findFirst({
    where: {
      name: SUPER_ADMIN_PERMISSION,
      guardName: GUARD,
      deletedAt: null,
    },
    select: { id: true },
  });

  if (!permission) {
    permission = await prisma.permissions.create({
      data: { name: SUPER_ADMIN_PERMISSION, guardName: GUARD },
      select: { id: true },
    });
  }

  const adminRole = rolesByName.get(SUPER_ADMIN_ROLE);
  if (adminRole) {
    await prisma.roleHasPermissions.upsert({
      where: {
        roleId_permissionId: {
          roleId: adminRole.id,
          permissionId: permission.id,
        },
      },
      update: {},
      create: { roleId: adminRole.id, permissionId: permission.id },
    });
  }

  for (const name of DEFAULT_PERMISSIONS) {
    const existing = await prisma.permissions.findFirst({
      where: { name, guardName: GUARD, deletedAt: null },
      select: { id: true },
    });

    if (!existing) {
      await prisma.permissions.create({
        data: { name, guardName: GUARD },
        select: { id: true },
      });
    }
  }

  for (const [roleName, permissionNames] of Object.entries(
    ROLE_PERMISSION_ASSIGNMENTS,
  )) {
    const role = rolesByName.get(roleName);
    if (!role) continue;

    for (const permissionName of permissionNames) {
      const rolePermission = await prisma.permissions.findFirst({
        where: {
          name: permissionName,
          guardName: GUARD,
          deletedAt: null,
        },
        select: { id: true },
      });

      if (!rolePermission) continue;

      await prisma.roleHasPermissions.upsert({
        where: {
          roleId_permissionId: {
            roleId: role.id,
            permissionId: rolePermission.id,
          },
        },
        update: {},
        create: {
          roleId: role.id,
          permissionId: rolePermission.id,
        },
      });
    }
  }

  return {
    permissionCount: DEFAULT_PERMISSIONS.length,
    superAdminPermission: SUPER_ADMIN_PERMISSION,
    superAdminRole: SUPER_ADMIN_ROLE,
  };
}
