import { ProjectMemberRole, SystemRole } from '@prisma/client';
import { prisma } from '../db';
import { forbidden } from './errors';

export type Action =
  | 'manage_employees'
  | 'create_project'
  | 'manage_project'
  | 'view_task'
  | 'edit_task'
  | 'change_task_status'
  | 'edit_requirements'
  | 'log_time'
  | 'upload_evidence'
  | 'create_bug'
  | 'manage_bug'
  | 'change_bug_status'
  | 'assign_work'
  | 'apply_leave'
  | 'manage_leave'
  | 'view_reports_all'
  | 'view_reports_project';

export type AuthUser = {
  id: string;
  code: string;
  name: string;
  email: string;
  role: SystemRole;
  active: boolean;
};

export type CanContext = {
  projectId?: string;
  projectAdminId?: string;
  isProjectMember?: boolean;
  projectRole?: ProjectMemberRole | null;
  isTaskAssignee?: boolean;
  taskStageDone?: boolean;
  /** Bug assignee (not parent-task assignee) */
  isBugAssignee?: boolean;
  /** Current bug status label, e.g. Ready for Retest */
  bugStatus?: string;
  /** Target status for change_bug_status checks */
  toStatus?: string;
};

export async function loadProjectContext(
  userId: string,
  projectId: string
): Promise<CanContext> {
  const project = await prisma.project.findUnique({
    where: { id: projectId },
    include: { members: { where: { employeeId: userId } } },
  });
  if (!project) return { projectId };
  const membership = project.members[0] || null;
  return {
    projectId,
    projectAdminId: project.adminId,
    isProjectMember: Boolean(membership) || project.adminId === userId,
    projectRole: membership?.projectRole ?? null,
  };
}

export function can(user: AuthUser, action: Action, ctx: CanContext = {}): boolean {
  if (!user.active) return false;
  if (user.role === SystemRole.SuperAdmin) return true;

  const isPA =
    user.role === SystemRole.ProjectAdmin ||
    ctx.projectRole === ProjectMemberRole.ProjectAdmin ||
    ctx.projectAdminId === user.id;

  switch (action) {
    case 'manage_employees':
    case 'manage_leave':
    case 'view_reports_all':
      return false;
    case 'create_project':
      return user.role === SystemRole.ProjectAdmin;
    case 'manage_project':
      return isPA && Boolean(ctx.isProjectMember || ctx.projectAdminId === user.id);
    case 'view_reports_project':
      return user.role === SystemRole.ProjectAdmin || isPA;
    case 'assign_work':
      return isPA;
    case 'log_time':
    case 'apply_leave':
      return true;
    case 'upload_evidence':
      return (
        isPA ||
        user.role === SystemRole.Developer ||
        user.role === SystemRole.BA ||
        user.role === SystemRole.TesterQA
      );
    case 'create_bug':
      return (
        (isPA || user.role === SystemRole.TesterQA) &&
        Boolean(ctx.isProjectMember || ctx.projectAdminId === user.id || isPA)
      );
    case 'edit_requirements':
      return isPA || user.role === SystemRole.BA;
    case 'view_task':
      return Boolean(ctx.isProjectMember || ctx.isTaskAssignee || isPA);
    case 'edit_task':
      if (ctx.taskStageDone && !isPA) return false;
      return Boolean(ctx.isProjectMember || ctx.isTaskAssignee || isPA);
    case 'change_task_status':
      return Boolean(isPA || ctx.isTaskAssignee);
    case 'manage_bug':
      return Boolean(
        isPA ||
          user.role === SystemRole.TesterQA ||
          ctx.isBugAssignee ||
          ctx.isProjectMember
      );
    case 'change_bug_status': {
      if (isPA || ctx.isBugAssignee) return true;
      // Tester/QA: only Close / Reopen from Ready for Retest
      if (
        user.role === SystemRole.TesterQA &&
        ctx.isProjectMember &&
        ctx.bugStatus === 'Ready for Retest' &&
        (ctx.toStatus === 'Closed' || ctx.toStatus === 'Reopened')
      ) {
        return true;
      }
      return false;
    }
    default:
      return false;
  }
}

export function assertCan(user: AuthUser, action: Action, ctx: CanContext = {}) {
  if (!can(user, action, ctx)) {
    throw forbidden(`You do not have permission to ${action.replace(/_/g, ' ')}`);
  }
}

/** Project visibility: SA all; others member/admin projects */
export async function visibleProjectIds(user: AuthUser): Promise<string[] | 'all'> {
  if (user.role === SystemRole.SuperAdmin) return 'all';
  const rows = await prisma.projectMember.findMany({
    where: { employeeId: user.id },
    select: { projectId: true },
  });
  const admin = await prisma.project.findMany({
    where: { adminId: user.id },
    select: { id: true },
  });
  return [...new Set([...rows.map((r) => r.projectId), ...admin.map((a) => a.id)])];
}
