import { HttpStatus } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { ALL_TAB_PERMISSIONS, normalizeTabPermissions } from './permissions';
import { AppException, ErrorCode } from './errors';

export type OrganizationTypeName = 'CLINIC' | 'LAB';

const CLINIC_ONLY_PERMISSIONS = new Set<string>([
  'TAB_PATIENTS_READ',
  'TAB_PATIENTS_EDIT',
  'TAB_APPOINTMENTS_READ',
  'TAB_APPOINTMENTS_EDIT',
  'TAB_TREATMENT_READ',
  'TAB_TREATMENT_EDIT',
]);

const LAB_ONLY_PERMISSIONS = new Set<string>([
  'TAB_CASES_READ',
  'TAB_CASES_EDIT',
  'TAB_TASKS_READ',
  'TAB_TASKS_EDIT',
]);

const SHARED_PERMISSIONS = ALL_TAB_PERMISSIONS.filter(
  (p) => !CLINIC_ONLY_PERMISSIONS.has(p) && !LAB_ONLY_PERMISSIONS.has(p),
);

export const CLINIC_TAB_PERMISSIONS = [
  ...SHARED_PERMISSIONS,
  ...CLINIC_ONLY_PERMISSIONS,
] as const;

export const LAB_TAB_PERMISSIONS = [
  ...SHARED_PERMISSIONS,
  ...LAB_ONLY_PERMISSIONS,
] as const;

const CLINIC_TAB_SET = new Set<string>(CLINIC_TAB_PERMISSIONS);
const LAB_TAB_SET = new Set<string>(LAB_TAB_PERMISSIONS);

export function permissionsAllowedForOrgType(orgType: OrganizationTypeName): Set<string> {
  return orgType === 'LAB' ? LAB_TAB_SET : CLINIC_TAB_SET;
}

export function filterPermissionsForOrgType(
  names: string[],
  orgType: OrganizationTypeName,
): string[] {
  const allowed = permissionsAllowedForOrgType(orgType);
  return normalizeTabPermissions(names.filter((n) => allowed.has(n)));
}

/** Owner opt-in permissions — granted via MembershipPermission when owner chooses to participate. */
const OWNER_OPT_IN_CLINIC = new Set<string>(['TAB_TREATMENT_READ', 'TAB_TREATMENT_EDIT']);
const OWNER_OPT_IN_LAB = new Set<string>(['TAB_TASKS_READ', 'TAB_TASKS_EDIT']);

function ownerBasePermissions(orgType: OrganizationTypeName): readonly string[] {
  const all = orgType === 'LAB' ? LAB_TAB_PERMISSIONS : CLINIC_TAB_PERMISSIONS;
  const optIn = orgType === 'LAB' ? OWNER_OPT_IN_LAB : OWNER_OPT_IN_CLINIC;
  return all.filter((p) => !optIn.has(p));
}

export function ownerPermissionsForOrgType(
  orgType: OrganizationTypeName,
  hasActivePlan: boolean,
): string[] {
  const base = ownerBasePermissions(orgType);

  if (hasActivePlan) {
    return [...base];
  }

  return normalizeTabPermissions(base.filter((p) => p.endsWith('_READ')));
}

export async function getOrganizationTypeName(
  prisma: PrismaService,
  organizationId: string,
): Promise<OrganizationTypeName> {
  const org = await prisma.organization.findUnique({
    where: { id: organizationId },
    select: { type: { select: { name: true } } },
  });

  if (!org) {
    throw new AppException(ErrorCode.PERMISSION_ORG_NOT_FOUND, HttpStatus.NOT_FOUND);
  }

  const name = org.type.name;
  if (name !== 'CLINIC' && name !== 'LAB') {
    throw new AppException(ErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN);
  }

  return name;
}

export async function assertClinicOrganization(
  prisma: PrismaService,
  organizationId: string,
): Promise<void> {
  const type = await getOrganizationTypeName(prisma, organizationId);
  if (type !== 'CLINIC') {
    throw new AppException(ErrorCode.PERMISSION_CLINIC_ONLY, HttpStatus.FORBIDDEN);
  }
}

export async function assertLabOrganization(
  prisma: PrismaService,
  organizationId: string,
): Promise<void> {
  const type = await getOrganizationTypeName(prisma, organizationId);
  if (type !== 'LAB') {
    throw new AppException(ErrorCode.PERMISSION_LAB_ONLY, HttpStatus.FORBIDDEN);
  }
}
