import { calculateAllocation } from "@capakraken/engine/allocation";
import type { WeekdayAvailability } from "@capakraken/shared";
import {
  calculateEffectiveBookedHours,
  loadResourceDailyAvailabilityContexts,
} from "../lib/resource-capacity.js";
import type { TRPCContext } from "../trpc.js";

export type ScenarioDb = TRPCContext["db"];

export type ScenarioChangeInput = {
  assignmentId?: string | undefined;
  resourceId?: string | undefined;
  roleId?: string | undefined;
  startDate: Date;
  endDate: Date;
  hoursPerDay: number;
  remove?: boolean | undefined;
};

export type ScenarioAvailability = {
  monday: number;
  tuesday: number;
  wednesday: number;
  thursday: number;
  friday: number;
  saturday: number;
  sunday: number;
};

export type ScenarioEntry = {
  resourceId: string | null;
  lcrCents: number;
  hoursPerDay: number;
  startDate: Date;
  endDate: Date;
  availability: ScenarioAvailability;
  isNew: boolean;
};

type ScenarioResourceContextInput = {
  id: string;
  availability: unknown;
  countryId: string | null;
  country?: { code: string } | null;
  federalState: string | null;
  metroCityId: string | null;
  metroCity?: { name: string } | null;
};

type ScenarioSkillsValue = Array<{ skill?: string | null }> | null | undefined;

export const DEFAULT_AVAILABILITY: ScenarioAvailability = {
  monday: 8,
  tuesday: 8,
  wednesday: 8,
  thursday: 8,
  friday: 8,
  saturday: 0,
  sunday: 0,
};

export const scenarioRoleSelect = {
  id: true,
  name: true,
  color: true,
} as const;

export const scenarioResourceSelect = {
  id: true,
  displayName: true,
  eid: true,
  lcrCents: true,
  availability: true,
  chargeabilityTarget: true,
  skills: true,
  countryId: true,
  federalState: true,
  metroCityId: true,
  country: { select: { code: true } },
  metroCity: { select: { name: true } },
} as const;

export const scenarioBaselineProjectSelect = {
  id: true,
  name: true,
  shortCode: true,
  startDate: true,
  endDate: true,
  budgetCents: true,
  orderType: true,
} as const;

export const scenarioSimulationProjectSelect = {
  id: true,
  name: true,
  budgetCents: true,
  orderType: true,
  startDate: true,
  endDate: true,
} as const;

export const scenarioBaselineAssignmentInclude = {
  resource: { select: scenarioResourceSelect },
  roleEntity: { select: scenarioRoleSelect },
} as const;

export const scenarioDemandInclude = {
  roleEntity: { select: scenarioRoleSelect },
} as const;

export const scenarioSimulationAssignmentInclude = {
  resource: { select: scenarioResourceSelect },
} as const;

export const scenarioUtilizationAssignmentSelect = {
  id: true,
  resourceId: true,
  projectId: true,
  hoursPerDay: true,
  startDate: true,
  endDate: true,
} as const;

export function getScenarioAvailability(availability: unknown): ScenarioAvailability {
  return (availability as ScenarioAvailability) ?? DEFAULT_AVAILABILITY;
}

export function roundToTenths(value: number): number {
  return Math.round(value * 10) / 10;
}

export function collectScenarioSkillSet(skills: ScenarioSkillsValue): Set<string> {
  const normalized = new Set<string>();
  for (const skill of skills ?? []) {
    if (typeof skill?.skill === "string" && skill.skill.trim().length > 0) {
      normalized.add(skill.skill.toLowerCase());
    }
  }
  return normalized;
}

function toResourceAvailabilityContextInput(resource: ScenarioResourceContextInput) {
  return {
    id: resource.id,
    availability: resource.availability as WeekdayAvailability,
    countryId: resource.countryId,
    countryCode: resource.country?.code,
    federalState: resource.federalState,
    metroCityId: resource.metroCityId,
    metroCityName: resource.metroCity?.name,
  };
}

export async function loadScenarioAvailabilityContexts(
  db: ScenarioDb,
  resources: ScenarioResourceContextInput[],
  periodStart: Date,
  periodEnd: Date,
) {
  return loadResourceDailyAvailabilityContexts(
    db,
    resources.map(toResourceAvailabilityContextInput),
    periodStart,
    periodEnd,
  );
}

export function calculateScenarioEntryHours(
  entry: Pick<ScenarioEntry, "resourceId" | "lcrCents" | "hoursPerDay" | "startDate" | "endDate" | "availability">,
  options: {
    periodStart: Date;
    periodEnd: Date;
    contexts: Awaited<ReturnType<typeof loadScenarioAvailabilityContexts>>;
  },
): number {
  if (!entry.resourceId) {
    return calculateAllocation({
      lcrCents: entry.lcrCents,
      hoursPerDay: entry.hoursPerDay,
      startDate: entry.startDate,
      endDate: entry.endDate,
      availability: entry.availability,
    }).totalHours;
  }

  return calculateEffectiveBookedHours({
    availability: entry.availability,
    startDate: entry.startDate,
    endDate: entry.endDate,
    hoursPerDay: entry.hoursPerDay,
    periodStart: options.periodStart,
    periodEnd: options.periodEnd,
    context: options.contexts.get(entry.resourceId),
  });
}
