import type { Bug } from './bug';
import type { DevTask } from './dev-task';
import type { OvertimeRecord } from './overtime';
import type { Requirement } from './requirement';
import type { TestCase } from './test-case';
import type { VersionPlan } from './version-plan';

export interface VersionDataScope {
  requirements: Requirement[];
  requirementIds: string[];
  requirementIdSet: Set<string>;
  plans: VersionPlan[];
  plansByType: Record<VersionPlan['type'], VersionPlan[]>;
  devTasks: DevTask[];
  testCases: TestCase[];
  bugs: Bug[];
  overtimeRecords: OvertimeRecord[];
}

interface VersionDataScopeInput {
  versionId: string;
  requirements: Requirement[];
  plans: VersionPlan[];
  devTasks: DevTask[];
  testCases: TestCase[];
  bugs: Bug[];
  overtimeRecords?: OvertimeRecord[];
}

interface VersionDataScopeMapInput extends Omit<VersionDataScopeInput, 'versionId'> {
  versionIds: string[];
}

export function buildVersionDataScope(input: VersionDataScopeInput): VersionDataScope {
  const {
    versionId,
    requirements,
    plans,
    devTasks,
    testCases,
    bugs,
    overtimeRecords = [],
  } = input;

  const scopedRequirements: Requirement[] = [];
  const requirementIds: string[] = [];
  const requirementIdSet = new Set<string>();
  for (const requirement of requirements) {
    if (requirement.versionId !== versionId) continue;
    scopedRequirements.push(requirement);
    requirementIds.push(requirement.id);
    requirementIdSet.add(requirement.id);
  }

  const scopedPlans: VersionPlan[] = [];
  const plansByType: Record<VersionPlan['type'], VersionPlan[]> = {
    research: [],
    product: [],
    ui: [],
  };
  for (const plan of plans) {
    if (plan.versionId !== versionId) continue;
    scopedPlans.push(plan);
    plansByType[plan.type].push(plan);
  }

  return {
    requirements: scopedRequirements,
    requirementIds,
    requirementIdSet,
    plans: scopedPlans,
    plansByType,
    devTasks: devTasks.filter((task) => task.versionId === versionId || (!task.versionId && requirementIdSet.has(task.requirementId))),
    testCases: testCases.filter((testCase) => testCase.versionId === versionId),
    bugs: bugs.filter((bug) => bug.versionId === versionId),
    overtimeRecords: overtimeRecords.filter((record) => record.versionId === versionId),
  };
}

export function hasVersionDataScopeRows(scope: VersionDataScope | null | undefined): scope is VersionDataScope {
  if (!scope) return false;
  return (
    scope.requirements.length > 0 ||
    scope.plans.length > 0 ||
    scope.devTasks.length > 0 ||
    scope.testCases.length > 0 ||
    scope.bugs.length > 0
  );
}

export function selectVersionDataScope(input: {
  appDataScope: VersionDataScope | null;
  v22Scope?: VersionDataScope | null;
}): VersionDataScope | null {
  const { appDataScope, v22Scope } = input;
  if (hasVersionDataScopeRows(v22Scope)) {
    return {
      ...v22Scope,
      overtimeRecords: appDataScope?.overtimeRecords ?? v22Scope.overtimeRecords,
    };
  }
  return appDataScope;
}

export function buildVersionDataScopeMap(input: VersionDataScopeMapInput): Record<string, VersionDataScope> {
  const scopes: Record<string, VersionDataScope> = {};
  const targetVersionIds = new Set(input.versionIds);
  for (const versionId of input.versionIds) {
    scopes[versionId] = createEmptyScope();
  }

  const requirementVersionMap = new Map<string, string>();
  for (const requirement of input.requirements) {
    const versionId = requirement.versionId;
    if (!versionId) continue;
    requirementVersionMap.set(requirement.id, versionId);
    const scope = scopes[versionId];
    if (!scope) continue;
    scope.requirements.push(requirement);
    scope.requirementIds.push(requirement.id);
    scope.requirementIdSet.add(requirement.id);
  }

  for (const plan of input.plans) {
    const scope = scopes[plan.versionId];
    if (!scope) continue;
    scope.plans.push(plan);
    scope.plansByType[plan.type].push(plan);
  }

  for (const task of input.devTasks) {
    const versionId = task.versionId || requirementVersionMap.get(task.requirementId);
    if (!versionId || !targetVersionIds.has(versionId)) continue;
    scopes[versionId]?.devTasks.push(task);
  }

  for (const testCase of input.testCases) {
    scopes[testCase.versionId]?.testCases.push(testCase);
  }

  for (const bug of input.bugs) {
    scopes[bug.versionId]?.bugs.push(bug);
  }

  for (const record of input.overtimeRecords ?? []) {
    if (!record.versionId) continue;
    scopes[record.versionId]?.overtimeRecords.push(record);
  }

  return scopes;
}

function createEmptyScope(): VersionDataScope {
  return {
    requirements: [],
    requirementIds: [],
    requirementIdSet: new Set<string>(),
    plans: [],
    plansByType: {
      research: [],
      product: [],
      ui: [],
    },
    devTasks: [],
    testCases: [],
    bugs: [],
    overtimeRecords: [],
  };
}
