import bcrypt from 'bcryptjs';
import {
  PrismaClient,
  ProjectStatus,
  SystemRole,
  ProjectMemberRole,
  TaskStage,
  BillingType,
  QaResult,
  BugSeverity,
  BugStatus,
  TimesheetCategory,
  TimesheetApproval,
  DayPeriod,
  LeaveRequestStatus,
} from '@prisma/client';
import {
  DEFAULT_INTERNAL_STATUSES,
  ensureReleasedInternalStatus,
} from '../src/lib/defaultInternalStatuses';

const prisma = new PrismaClient();

const DEMO_PASSWORD = 'Password123!';

function daysFromNow(days: number): Date {
  const d = new Date();
  d.setUTCHours(12, 0, 0, 0);
  d.setUTCDate(d.getUTCDate() + days);
  return d;
}

async function main() {
  console.log('Seeding ZPT demo data...');

  // Clear in FK-safe order (Phase 1 entities + shared counters)
  await prisma.savedFavorite.deleteMany();
  await prisma.notification.deleteMany();
  await prisma.attachment.deleteMany();
  await prisma.activeTimer.deleteMany();
  await prisma.note.deleteMany();
  await prisma.plannedActivity.deleteMany();
  await prisma.activityLog.deleteMany();
  await prisma.timesheet.deleteMany();
  await prisma.bug.deleteMany();
  await prisma.taskAssignment.deleteMany();
  await prisma.task.deleteMany();
  await prisma.projectInternalStatus.deleteMany();
  await prisma.projectMember.deleteMany();
  await prisma.project.deleteMany();
  await prisma.client.deleteMany();
  await prisma.leaveRequest.deleteMany();
  await prisma.leaveAllocation.deleteMany();
  await prisma.leaveType.deleteMany();
  await prisma.holiday.deleteMany();
  await prisma.employee.deleteMany();
  await prisma.idCounter.deleteMany();

  const passwordHash = await bcrypt.hash(DEMO_PASSWORD, 10);

  const employees = await Promise.all(
    [
      {
        code: 'EMP-001',
        name: 'Gowtham',
        email: 'gowtham@zettai.demo',
        phone: '+91 90000 10001',
        department: 'Leadership',
        designation: 'CTO',
        role: SystemRole.SuperAdmin,
        active: true,
        avatarColor: '#256a92',
      },
      {
        code: 'EMP-002',
        name: 'Priya',
        email: 'priya@zettai.demo',
        phone: '+91 90000 10002',
        department: 'PMO',
        designation: 'Project Manager',
        role: SystemRole.ProjectAdmin,
        active: true,
        avatarColor: '#2a9d6c',
      },
      {
        code: 'EMP-003',
        name: 'Arun',
        email: 'arun@zettai.demo',
        phone: '+91 90000 10003',
        department: 'BA',
        designation: 'Business Analyst',
        role: SystemRole.BA,
        active: true,
        avatarColor: '#c98512',
      },
      {
        code: 'EMP-004',
        name: 'Karthik',
        email: 'karthik@zettai.demo',
        phone: '+91 90000 10004',
        department: 'Engineering',
        designation: 'Senior Developer',
        role: SystemRole.Developer,
        active: true,
        avatarColor: '#3C8DBC',
      },
      {
        code: 'EMP-005',
        name: 'Nisha',
        email: 'nisha@zettai.demo',
        phone: '+91 90000 10005',
        department: 'QA',
        designation: 'QA Engineer',
        role: SystemRole.TesterQA,
        active: true,
        avatarColor: '#c4452f',
      },
      {
        code: 'EMP-006',
        name: 'Meena',
        email: 'meena@zettai.demo',
        phone: '+91 90000 10006',
        department: 'Engineering',
        designation: 'Developer',
        role: SystemRole.Developer,
        active: true,
        avatarColor: '#6610f2',
      },
      {
        code: 'EMP-007',
        name: 'Vikram',
        email: 'vikram@zettai.demo',
        phone: '+91 90000 10007',
        department: 'Engineering',
        designation: 'Developer',
        role: SystemRole.Developer,
        active: false,
        avatarColor: '#6c757d',
      },
    ].map((e) =>
      prisma.employee.create({
        data: { ...e, passwordHash },
      })
    )
  );

  const byCode = Object.fromEntries(employees.map((e) => [e.code, e]));

  await prisma.idCounter.createMany({
    data: [
      { key: 'emp', value: 7 },
      { key: 'prj', value: 2 },
      { key: 'client', value: 2 },
    ],
  });

  const zettaiClient = await prisma.client.create({
    data: {
      id: 'c1111111-1111-4111-8111-111111111101',
      code: 'CLT-001',
      name: 'Zettai Systems',
      contactName: 'Gowtham',
      email: 'gowtham@zettai.demo',
      address: 'Internal',
      active: true,
    },
  });
  const meridianClient = await prisma.client.create({
    data: {
      id: 'c1111111-1111-4111-8111-111111111102',
      code: 'CLT-002',
      name: 'Meridian Textiles',
      contactName: 'Priya',
      email: 'priya@zettai.demo',
      active: true,
    },
  });

  await prisma.leaveType.createMany({
    data: [
      { id: 'cl', code: 'CL', label: 'Casual Leave (CL)', tracksBalance: true },
      { id: 'el', code: 'EL', label: 'Earned Leave (EL)', tracksBalance: true },
      { id: 'sl', code: 'SL', label: 'Sick Time Off (SL)', tracksBalance: true },
      { id: 'comp', code: 'COMP', label: 'Compensatory Off', tracksBalance: true },
      { id: 'unpaid', code: 'UNPAID', label: 'Unpaid', tracksBalance: false },
    ],
  });

  async function createProject(opts: {
    humanCode: string;
    code: string;
    name: string;
    description: string;
    requireRequirementDoc: boolean;
    allowTypedEvidence: boolean;
    color: string;
    tags: string[];
    members: Array<{ code: string; role: ProjectMemberRole }>;
    clientId: string;
  }) {
    return prisma.project.create({
      data: {
        humanCode: opts.humanCode,
        code: opts.code,
        name: opts.name,
        description: opts.description,
        clientId: opts.clientId,
        startDate: daysFromNow(opts.code === 'ZPT' ? -45 : -30),
        endDate: daysFromNow(opts.code === 'ZPT' ? 60 : 90),
        adminId: byCode['EMP-002'].id,
        status: ProjectStatus.Active,
        color: opts.color,
        tags: opts.tags,
        requireRequirementDoc: opts.requireRequirementDoc,
        allowTypedEvidence: opts.allowTypedEvidence,
        members: {
          create: opts.members.map((m) => ({
            employeeId: byCode[m.code].id,
            projectRole: m.role,
          })),
        },
        internalStatuses: {
          create: DEFAULT_INTERNAL_STATUSES.map((s) => ({
            text: s.text,
            color: s.color,
            stage: s.stage as TaskStage,
            sortOrder: s.sortOrder,
            active: true,
          })),
        },
      },
      include: { internalStatuses: true },
    });
  }

  const zpt = await createProject({
    humanCode: 'PRJ-001',
    code: 'ZPT',
    name: 'ZPT — Zettai Process Tracker',
    description: 'Internal delivery workflow tracker for Zettai Systems teams.',
    requireRequirementDoc: true,
    allowTypedEvidence: false,
    color: '#3C8DBC',
    tags: ['internal', 'poc'],
    clientId: zettaiClient.id,
    members: [
      { code: 'EMP-002', role: ProjectMemberRole.ProjectAdmin },
      { code: 'EMP-003', role: ProjectMemberRole.BA },
      { code: 'EMP-004', role: ProjectMemberRole.Developer },
      { code: 'EMP-005', role: ProjectMemberRole.TesterQA },
      { code: 'EMP-006', role: ProjectMemberRole.Developer },
    ],
  });

  const portal = await createProject({
    humanCode: 'PRJ-002',
    code: 'PORTAL',
    name: 'Customer Portal',
    description: 'Client-facing portal for Meridian Textiles.',
    requireRequirementDoc: false,
    allowTypedEvidence: true,
    color: '#2a9d6c',
    tags: ['client', 'portal'],
    clientId: meridianClient.id,
    members: [
      { code: 'EMP-002', role: ProjectMemberRole.ProjectAdmin },
      { code: 'EMP-003', role: ProjectMemberRole.BA },
      { code: 'EMP-004', role: ProjectMemberRole.Developer },
      { code: 'EMP-005', role: ProjectMemberRole.TesterQA },
    ],
  });

  function findStatus(project: typeof zpt, text: string) {
    return (project.internalStatuses.find((x) => x.text === text) || project.internalStatuses[0]).id;
  }

  type SeedTask = {
    code: string;
    title: string;
    project: typeof zpt;
    description: string;
    acceptanceCriteria: string;
    stage: TaskStage;
    statusText: string;
    priority: number;
    tags: string[];
    assigneeCode?: string | null;
    assignmentCodes?: Array<{ code: string; hours: number }>;
    allocatedHours: number;
    billingType: BillingType;
    dueDays?: number | null;
    startDays?: number | null;
    parentCode?: string | null;
    required?: boolean;
    baAnalysisComplete?: boolean;
    developmentComplete?: boolean;
    unitTestEvidence?: object | null;
    developerCompletionLog?: string;
    codeReviewApproved?: boolean;
    qaResult?: QaResult | null;
    qaEvidence?: object | null;
    acceptanceSatisfied?: boolean;
    testerApproval?: boolean;
    doneDays?: number | null;
    reqAttachment?: boolean;
  };

  const seedTasks: SeedTask[] = [
    {
      code: 'ZPT-101',
      title: 'Employee CRUD API',
      project: zpt,
      description: 'REST endpoints for employee create, update, activate/deactivate.',
      acceptanceCriteria: 'CRUD works with validation; inactive employees retained in history.',
      stage: TaskStage.InProgress,
      statusText: 'Development',
      priority: 2,
      tags: ['api', 'backend'],
      assignmentCodes: [
        { code: 'EMP-004', hours: 16 },
        { code: 'EMP-006', hours: 8 },
      ],
      allocatedHours: 24,
      billingType: BillingType.Billable,
      startDays: -14,
      dueDays: 7,
      baAnalysisComplete: true,
      reqAttachment: true,
    },
    {
      code: 'ZPT-102',
      title: 'Login UI',
      project: zpt,
      description: 'Demo role-switcher login screen with sample users.',
      acceptanceCriteria: 'User can switch roles without resetting data; current user shown in shell.',
      stage: TaskStage.Backlog,
      statusText: 'Ready for Development',
      priority: 3,
      tags: ['ui', 'auth'],
      assignmentCodes: [{ code: 'EMP-004', hours: 16 }],
      allocatedHours: 16,
      billingType: BillingType.NonBillable,
      startDays: -5,
      dueDays: 10,
      baAnalysisComplete: true,
      reqAttachment: true,
    },
    {
      code: 'ZPT-103',
      title: 'Role Permission Validation',
      project: zpt,
      description: 'Front-end permission matrix for demo roles.',
      acceptanceCriteria: 'Nav and actions change by role; Super Admin sees all.',
      stage: TaskStage.Backlog,
      statusText: 'Clarification Needed',
      priority: 1,
      tags: ['rbac'],
      assigneeCode: null,
      allocatedHours: 0,
      billingType: BillingType.NonBillable,
      dueDays: 14,
    },
    {
      code: 'ZPT-104',
      title: 'Unit Test Coverage',
      project: zpt,
      description: 'Raise coverage for workflow gate validators.',
      acceptanceCriteria: 'Gate A/B/C unit tests pass; evidence attached.',
      stage: TaskStage.CodeReviewQA,
      statusText: 'QA Testing',
      priority: 2,
      tags: ['testing', 'quality'],
      assigneeCode: 'EMP-004',
      allocatedHours: 12,
      billingType: BillingType.Billable,
      startDays: -18,
      dueDays: -2,
      baAnalysisComplete: true,
      developmentComplete: true,
      unitTestEvidence: { type: 'file', note: 'Coverage report attached', at: new Date().toISOString() },
      developerCompletionLog: 'Gate validators covered; all suites green.',
      codeReviewApproved: true,
      reqAttachment: true,
    },
    {
      code: 'ZPT-105',
      title: 'QA Regression',
      project: zpt,
      description: 'Full regression pack for process tracker happy path.',
      acceptanceCriteria: 'Walkthrough passes; blocking bugs closed.',
      stage: TaskStage.CodeReviewQA,
      statusText: 'Bug Fixing',
      priority: 3,
      tags: ['qa', 'regression'],
      assigneeCode: 'EMP-005',
      allocatedHours: 20,
      billingType: BillingType.Billable,
      startDays: -10,
      dueDays: 3,
      baAnalysisComplete: true,
      developmentComplete: true,
      unitTestEvidence: { type: 'file', note: 'Smoke suite attached', at: new Date().toISOString() },
      developerCompletionLog: 'Feature complete for regression.',
      codeReviewApproved: true,
      qaResult: QaResult.Failed,
      qaEvidence: { type: 'typed', note: 'Login role switch failed on tablet width', at: new Date().toISOString() },
      reqAttachment: true,
    },
    {
      code: 'ZPT-106',
      title: 'Dashboard Widgets',
      project: zpt,
      description: 'Super Admin and My Work dashboard counters.',
      acceptanceCriteria: 'Counters recalculate from live demo data.',
      stage: TaskStage.Done,
      statusText: 'Completed',
      priority: 1,
      tags: ['ui', 'reports'],
      assigneeCode: 'EMP-006',
      allocatedHours: 18,
      billingType: BillingType.NonBillable,
      startDays: -40,
      dueDays: -10,
      baAnalysisComplete: true,
      developmentComplete: true,
      unitTestEvidence: { type: 'typed', note: 'Widget unit checks passed', at: new Date().toISOString() },
      developerCompletionLog: 'All widgets wired to store.',
      codeReviewApproved: true,
      qaResult: QaResult.Passed,
      qaEvidence: { type: 'typed', note: 'QA pass log', at: new Date().toISOString() },
      acceptanceSatisfied: true,
      testerApproval: true,
      doneDays: -10,
      reqAttachment: true,
    },
    {
      code: 'ZPT-107',
      title: 'Timesheet Rollups',
      project: zpt,
      description: 'Parent task with subtasks for analysis, development, and QA.',
      acceptanceCriteria: 'Child allocations roll up; warn if children exceed parent.',
      stage: TaskStage.InProgress,
      statusText: 'Development',
      priority: 2,
      tags: ['timesheets'],
      assignmentCodes: [
        { code: 'EMP-004', hours: 20 },
        { code: 'EMP-003', hours: 8 },
        { code: 'EMP-005', hours: 12 },
      ],
      allocatedHours: 40,
      billingType: BillingType.Billable,
      startDays: -7,
      dueDays: 21,
      baAnalysisComplete: true,
      reqAttachment: true,
    },
    {
      code: 'ZPT-108',
      title: 'Analysis subtask',
      project: zpt,
      description: 'BA breakdown of rollup rules.',
      acceptanceCriteria: 'Rules documented and accepted.',
      stage: TaskStage.Done,
      statusText: 'Completed',
      priority: 1,
      tags: ['subtask'],
      assigneeCode: 'EMP-003',
      allocatedHours: 8,
      billingType: BillingType.Billable,
      parentCode: 'ZPT-107',
      required: true,
      baAnalysisComplete: true,
      developmentComplete: true,
      codeReviewApproved: true,
      qaResult: QaResult.Passed,
      acceptanceSatisfied: true,
      testerApproval: true,
      doneDays: -3,
    },
    {
      code: 'ZPT-109',
      title: 'Dev implementation subtask',
      project: zpt,
      description: 'Implement rollup calculations.',
      acceptanceCriteria: 'No double-counting of bug hours.',
      stage: TaskStage.InProgress,
      statusText: 'Development',
      priority: 2,
      tags: ['subtask'],
      assigneeCode: 'EMP-004',
      allocatedHours: 20,
      billingType: BillingType.Billable,
      parentCode: 'ZPT-107',
      required: true,
      baAnalysisComplete: true,
    },
    {
      code: 'ZPT-110',
      title: 'QA verification subtask',
      project: zpt,
      description: 'Verify rollups in reports.',
      acceptanceCriteria: 'Over-budget indicators correct.',
      stage: TaskStage.Backlog,
      statusText: 'Created',
      priority: 1,
      tags: ['subtask'],
      assigneeCode: 'EMP-005',
      allocatedHours: 12,
      billingType: BillingType.Billable,
      parentCode: 'ZPT-107',
      required: true,
    },
    {
      code: 'PORTAL-201',
      title: 'Portal Login Screen',
      project: portal,
      description: 'Customer login with email/password.',
      acceptanceCriteria: 'Valid credentials redirect to dashboard.',
      stage: TaskStage.Backlog,
      statusText: 'Ready for Development',
      priority: 2,
      tags: ['portal', 'auth'],
      assigneeCode: 'EMP-004',
      allocatedHours: 10,
      billingType: BillingType.Billable,
      startDays: -2,
      dueDays: 12,
      baAnalysisComplete: true,
      reqAttachment: true,
    },
  ];

  const createdByCode: Record<string, { id: string }> = {};

  for (const t of seedTasks) {
    const assignments =
      t.assignmentCodes?.map((a) => ({
        employeeId: byCode[a.code].id,
        allocatedHours: a.hours,
      })) ||
      (t.assigneeCode
        ? [{ employeeId: byCode[t.assigneeCode].id, allocatedHours: t.allocatedHours }]
        : []);
    const assigneeId = assignments[0]?.employeeId || null;
    const parentId = t.parentCode ? createdByCode[t.parentCode].id : null;

    const row = await prisma.task.create({
      data: {
        code: t.code,
        title: t.title,
        projectId: t.project.id,
        description: t.description,
        acceptanceCriteria: t.acceptanceCriteria,
        stage: t.stage,
        internalStatusId: findStatus(t.project, t.statusText),
        priority: t.priority,
        tags: t.tags,
        assigneeId,
        allocatedHours: t.allocatedHours,
        billingType: t.billingType,
        startDate: t.startDays != null ? daysFromNow(t.startDays) : null,
        dueDate: t.dueDays != null ? daysFromNow(t.dueDays) : null,
        parentId,
        required: t.required ?? false,
        baAnalysisComplete: t.baAnalysisComplete ?? false,
        developmentComplete: t.developmentComplete ?? false,
        unitTestEvidence: t.unitTestEvidence ?? undefined,
        developerCompletionLog: t.developerCompletionLog || null,
        codeReviewApproved: t.codeReviewApproved ?? false,
        qaResult: t.qaResult ?? null,
        qaEvidence: t.qaEvidence ?? undefined,
        acceptanceSatisfied: t.acceptanceSatisfied ?? false,
        testerApproval: t.testerApproval ?? false,
        completionDate: t.doneDays != null ? daysFromNow(t.doneDays) : null,
        completedById: t.doneDays != null ? byCode['EMP-005'].id : null,
        assignments: { create: assignments },
        attachments: t.reqAttachment
          ? {
              create: [
                {
                  name: `${t.code}-requirement.pdf`,
                  size: 12000,
                  mimeType: 'application/pdf',
                  kind: 'requirement',
                  storagePath: `seed/${t.code}-requirement.pdf`,
                },
              ],
            }
          : undefined,
      },
    });
    createdByCode[t.code] = row;
  }

  // Wire subtask dependency ZPT-110 → ZPT-109
  await prisma.task.update({
    where: { id: createdByCode['ZPT-110'].id },
    data: { dependencies: [createdByCode['ZPT-109'].id] },
  });

  const byEmpCode: Record<string, string> = {};
  for (const e of await prisma.employee.findMany({ select: { id: true, code: true } })) {
    byEmpCode[e.code] = e.id;
  }

  const seedBugs = [
    {
      code: 'BUG-001',
      parentCode: 'ZPT-105',
      title: 'Role switcher truncates on tablet',
      description: 'User menu overflows at 768px width.',
      reproSteps: '1. Open app at 768px\n2. Open user menu\n3. Observe truncation',
      expected: 'Full name and role visible',
      actual: 'Role label cut off',
      severity: BugSeverity.High,
      priority: 2,
      assigneeCode: 'EMP-004',
      reporterCode: 'EMP-005',
      allocatedHours: 4,
      status: BugStatus.InProgress,
      blocking: true,
      resolutionNotes: '',
    },
    {
      code: 'BUG-002',
      parentCode: 'ZPT-104',
      title: 'Coverage badge shows stale %',
      description: 'Badge not refreshing after suite run.',
      reproSteps: 'Run suite then open task detail',
      expected: 'Updated percentage',
      actual: 'Previous value cached',
      severity: BugSeverity.Medium,
      priority: 1,
      assigneeCode: 'EMP-004',
      reporterCode: 'EMP-005',
      allocatedHours: 2,
      status: BugStatus.ReadyForRetest,
      blocking: false,
      resolutionNotes: 'Invalidated cache on suite complete',
      withEvidence: true,
    },
    {
      code: 'BUG-003',
      parentCode: 'ZPT-106',
      title: 'Overdue counter off-by-one',
      description: 'Tasks due today counted as overdue.',
      reproSteps: 'Create task due today; open dashboard',
      expected: 'Not overdue until after due date',
      actual: 'Counted as overdue',
      severity: BugSeverity.Low,
      priority: 1,
      assigneeCode: 'EMP-006',
      reporterCode: 'EMP-005',
      allocatedHours: 1,
      status: BugStatus.Closed,
      blocking: false,
      resolutionNotes: 'Compare date-only strings',
    },
  ] as const;

  for (const b of seedBugs) {
    const parent = createdByCode[b.parentCode];
    if (!parent) throw new Error('Missing parent task for ' + b.code);
    const assigneeId = byEmpCode[b.assigneeCode] || null;
    const bug = await prisma.bug.create({
      data: {
        code: b.code,
        parentTaskId: parent.id,
        title: b.title,
        description: b.description,
        reproSteps: b.reproSteps,
        expected: b.expected,
        actual: b.actual,
        severity: b.severity,
        priority: b.priority,
        assigneeId,
        reporterId: byEmpCode[b.reporterCode],
        allocatedHours: b.allocatedHours,
        status: b.status,
        blocking: b.blocking,
        resolutionNotes: b.resolutionNotes,
        assignments: assigneeId
          ? {
              create: [{
                employeeId: assigneeId,
                allocatedHours: b.allocatedHours,
              }],
            }
          : undefined,
      },
    });

    if ('withEvidence' in b && b.withEvidence) {
      const att = await prisma.attachment.create({
        data: {
          name: 'coverage-suite-results.log',
          size: 12400,
          mimeType: 'text/plain',
          kind: 'unit-test',
          storagePath: 'seed/coverage-suite-results.log',
          bugId: bug.id,
          uploadedById: byEmpCode['EMP-004'],
        },
      });
      await prisma.bug.update({
        where: { id: bug.id },
        data: {
          unitTestEvidence: {
            type: 'file',
            note: 'Coverage suite re-run after cache invalidation',
            attachmentId: att.id,
            at: new Date().toISOString(),
            uploadedBy: byEmpCode['EMP-004'],
          },
        },
      });
    }
  }

  const createdBugsByCode: Record<string, { id: string }> = {};
  for (const b of await prisma.bug.findMany({ select: { id: true, code: true } })) {
    createdBugsByCode[b.code] = { id: b.id };
  }

  const seedTimesheets: {
    emp: string;
    task: string;
    bug?: string;
    days: number;
    hours: number;
    category: TimesheetCategory;
    description: string;
    approval: TimesheetApproval;
  }[] = [
    { emp: 'EMP-004', task: 'ZPT-101', days: -3, hours: 6, category: TimesheetCategory.Development, description: 'CRUD endpoints scaffold', approval: TimesheetApproval.Approved },
    { emp: 'EMP-004', task: 'ZPT-101', days: -2, hours: 5, category: TimesheetCategory.Development, description: 'Validation + deactivate', approval: TimesheetApproval.Approved },
    { emp: 'EMP-004', task: 'ZPT-101', days: -1, hours: 4, category: TimesheetCategory.UnitTesting, description: 'API tests', approval: TimesheetApproval.Pending },
    { emp: 'EMP-003', task: 'ZPT-102', days: -4, hours: 3, category: TimesheetCategory.Analysis, description: 'Login UX review', approval: TimesheetApproval.Approved },
    { emp: 'EMP-004', task: 'ZPT-104', days: -6, hours: 8, category: TimesheetCategory.UnitTesting, description: 'Gate coverage', approval: TimesheetApproval.Approved },
    { emp: 'EMP-005', task: 'ZPT-104', days: -2, hours: 3, category: TimesheetCategory.QA, description: 'Started QA pass', approval: TimesheetApproval.Pending },
    { emp: 'EMP-005', task: 'ZPT-105', days: -3, hours: 5, category: TimesheetCategory.QA, description: 'Regression cycle 1', approval: TimesheetApproval.Approved },
    { emp: 'EMP-004', task: 'ZPT-105', bug: 'BUG-001', days: -1, hours: 3, category: TimesheetCategory.BugFixing, description: 'Tablet menu layout fix', approval: TimesheetApproval.Pending },
    { emp: 'EMP-006', task: 'ZPT-106', days: -25, hours: 7, category: TimesheetCategory.Development, description: 'Dashboard widgets', approval: TimesheetApproval.Approved },
    { emp: 'EMP-006', task: 'ZPT-106', days: -20, hours: 8, category: TimesheetCategory.Development, description: 'Counters + charts', approval: TimesheetApproval.Approved },
    { emp: 'EMP-006', task: 'ZPT-106', days: -15, hours: 6, category: TimesheetCategory.Development, description: 'My Work view', approval: TimesheetApproval.Approved },
    { emp: 'EMP-005', task: 'ZPT-106', days: -11, hours: 4, category: TimesheetCategory.QA, description: 'Dashboard QA', approval: TimesheetApproval.Approved },
    { emp: 'EMP-003', task: 'ZPT-108', days: -6, hours: 4, category: TimesheetCategory.Analysis, description: 'Rollup rules', approval: TimesheetApproval.Approved },
    { emp: 'EMP-004', task: 'ZPT-109', days: -2, hours: 6, category: TimesheetCategory.Development, description: 'Rollup engine', approval: TimesheetApproval.Pending },
    { emp: 'EMP-004', task: 'ZPT-109', days: -1, hours: 5, category: TimesheetCategory.Development, description: 'Variance indicators', approval: TimesheetApproval.Pending },
    { emp: 'EMP-004', task: 'PORTAL-201', days: 0, hours: 2, category: TimesheetCategory.Development, description: 'Portal login scaffold', approval: TimesheetApproval.Pending },
  ];

  for (const ts of seedTimesheets) {
    const task = createdByCode[ts.task];
    if (!task) throw new Error('Missing task for timesheet ' + ts.task);
    const fullTask = await prisma.task.findUniqueOrThrow({ where: { id: task.id } });
    await prisma.timesheet.create({
      data: {
        employeeId: byEmpCode[ts.emp],
        projectId: fullTask.projectId,
        taskId: fullTask.id,
        bugId: ts.bug ? createdBugsByCode[ts.bug]?.id || null : null,
        workDate: daysFromNow(ts.days),
        hours: ts.hours,
        category: ts.category,
        description: ts.description,
        billingType: fullTask.billingType,
        approvalStatus: ts.approval,
      },
    });
  }

  const year = new Date().getFullYear();
  const validUntil = new Date(`${year}-12-31T00:00:00.000Z`);
  const activeEmps = employees.filter((e) => e.active);
  let allocCount = 0;
  for (let idx = 0; idx < activeEmps.length; idx++) {
    const emp = activeEmps[idx];
    const packs: Array<[string, number]> = [
      ['cl', 7],
      ['el', 12],
      ['sl', 12],
      ['comp', idx === 0 ? 1 : 0],
    ];
    for (const [typeId, allocated] of packs) {
      await prisma.leaveAllocation.create({
        data: {
          employeeId: emp.id,
          typeId,
          year,
          allocated,
          validUntil,
        },
      });
      allocCount += 1;
    }
  }

  await prisma.leaveRequest.createMany({
    data: [
      {
        employeeId: byEmpCode['EMP-004'],
        typeId: 'cl',
        fromDate: daysFromNow(3),
        toDate: daysFromNow(3),
        dayPeriod: DayPeriod.full,
        days: 1,
        description: 'Personal work',
        status: LeaveRequestStatus.to_approve,
      },
      {
        employeeId: byEmpCode['EMP-006'],
        typeId: 'el',
        fromDate: daysFromNow(10),
        toDate: daysFromNow(11),
        dayPeriod: DayPeriod.full,
        days: 2,
        description: 'Family travel',
        status: LeaveRequestStatus.validated,
        decidedById: byEmpCode['EMP-001'],
        decidedAt: new Date(),
        decisionNote: 'Approved',
      },
    ],
  });

  const holidays = [
    { date: `${year}-01-01`, name: 'New Year' },
    { date: `${year}-01-26`, name: 'Republic Day' },
    { date: `${year}-08-15`, name: 'Independence Day' },
    { date: `${year}-10-02`, name: 'Gandhi Jayanti' },
    { date: `${year}-12-25`, name: 'Christmas' },
  ];
  for (const h of holidays) {
    await prisma.holiday.create({
      data: {
        date: new Date(h.date + 'T00:00:00.000Z'),
        name: h.name,
      },
    });
  }

  await prisma.notification.createMany({
    data: [
      {
        employeeId: byEmpCode['EMP-001'],
        text: 'BUG-001 is blocking QA Regression',
        read: false,
      },
      {
        employeeId: byEmpCode['EMP-001'],
        text: 'ZPT-104 is overdue for QA',
        read: false,
      },
      {
        employeeId: byEmpCode['EMP-004'],
        text: 'Leave request for Meena was approved',
        read: true,
      },
    ],
  });

  await prisma.idCounter.createMany({
    data: [
      { key: 'task_ZPT', value: 110 },
      { key: 'task_PORTAL', value: 201 },
      { key: 'bug', value: 3 },
    ],
  });

  await ensureReleasedInternalStatus(prisma);

  console.log('Seed complete.');
  console.log(`Demo password for all users: ${DEMO_PASSWORD}`);
  console.log('Super Admin: gowtham@zettai.demo');
  console.log(`Tasks seeded: ${seedTasks.length}`);
  console.log(`Bugs seeded: ${seedBugs.length}`);
  console.log(`Timesheets seeded: ${seedTimesheets.length}`);
  console.log(`Leave allocations seeded: ${allocCount}`);
  console.log(`Holidays seeded: ${holidays.length}`);
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
