import type { AgentDecomposeTarget } from '@ftb/shared';

export type PlanTaskStatus = 'pending' | 'in_progress' | 'completed';
export type ProductPlanKind = 'design' | 'review';
export type ProductPlanReviewResult = 'passed' | 'failed';
export type RequirementCoverageStatus = 'not_started' | 'partial' | 'completed';
export type VersionPlanLogType = 'requirement_progress' | 'research_direction_progress' | 'ai_decompose' | 'system';
export type AiDecomposeLogStatus = 'started' | 'completed' | 'error';
export type ProductPlanReviewFailureType =
  | 'requirement_mismatch'
  | 'information_architecture'
  | 'interaction_flow'
  | 'state_coverage'
  | 'edge_case_missing'
  | 'business_rule_gap'
  | 'role_permission_gap'
  | 'data_rule_gap'
  | 'copywriting_ambiguity'
  | 'risk_dependency';

export const PRODUCT_PLAN_KIND_LABEL: Record<ProductPlanKind, string> = {
  design: '设计方案',
  review: '方案评审',
};

export const PRODUCT_PLAN_REVIEW_RESULT_LABEL: Record<ProductPlanReviewResult, string> = {
  passed: '评审通过',
  failed: '评审不通过',
};

export const PRODUCT_PLAN_REVIEW_FAILURE_OPTIONS: { value: ProductPlanReviewFailureType; label: string }[] = [
  { value: 'requirement_mismatch', label: '需求覆盖不完整/偏离需求' },
  { value: 'information_architecture', label: '信息架构或页面层级不清晰' },
  { value: 'interaction_flow', label: '关键交互流程不闭环' },
  { value: 'state_coverage', label: '缺少状态、异常或空数据场景' },
  { value: 'edge_case_missing', label: '边界场景考虑不足' },
  { value: 'business_rule_gap', label: '业务规则、审批或口径缺失' },
  { value: 'role_permission_gap', label: '角色权限与可见范围不明确' },
  { value: 'data_rule_gap', label: '字段、数据来源或计算逻辑不清楚' },
  { value: 'copywriting_ambiguity', label: '文案表达有歧义或误导' },
  { value: 'risk_dependency', label: '依赖、风险或上线影响未说明' },
];

export interface PlanTask {
  id: string;
  title: string;
  status: PlanTaskStatus;
  completedContent?: string;
  remainingContent?: string;
  currentWorkStartedAt?: string;
  updatedAt?: string;
  updatedBy?: string;
}

export const RESEARCH_DIRECTION_PRESETS = [
  '\u7ade\u54c1\u5206\u6790',
  '\u7528\u6237\u8bbf\u8c08',
  '\u6570\u636e\u8c03\u7814',
  '\u6280\u672f\u53ef\u884c\u6027\u5206\u6790',
  '\u5e02\u573a\u8c03\u7814',
  '\u9700\u6c42\u5206\u6790',
];

export function getResearchDirectionPresetOptions(tasks: Array<Pick<PlanTask, 'title'>>): Array<{
  title: string;
  disabled: boolean;
}> {
  const selectedTitles = new Set(tasks.map((task) => task.title.trim()).filter(Boolean));
  return RESEARCH_DIRECTION_PRESETS.map((title) => ({
    title,
    disabled: selectedTitles.has(title),
  }));
}

export interface VersionPlanRequirementCoverage {
  requirementId: string;
  status: RequirementCoverageStatus;
  completedContent?: string;
  remainingContent?: string;
  currentWorkStartedAt?: string;
  updatedAt: string;
  updatedBy: string;
}

export interface VersionPlanLog {
  id: string;
  type: VersionPlanLogType;
  createdAt: string;
  actor: string;
  title: string;
  detail?: string;
  requirementId?: string;
  requirementCode?: string;
  requirementTitle?: string;
  coverageStatus?: RequirementCoverageStatus;
  completedContent?: string;
  remainingContent?: string;
  workStartedAt?: string;
  directionTaskId?: string;
  directionTitle?: string;
  aiTarget?: AgentDecomposeTarget;
  aiStatus?: AiDecomposeLogStatus;
}

export interface RequirementCoverageWorkStartInput {
  requirementId: string;
  startedAt?: string;
  updatedBy: string;
}

export interface RequirementCoverageUpdateInput {
  requirementId: string;
  status: RequirementCoverageStatus;
  completedContent?: string;
  remainingContent?: string;
  workStartedAt?: string;
  updatedAt?: string;
  updatedBy: string;
  requirementCode?: string;
  requirementTitle?: string;
}

export interface ResearchDirectionWorkStartInput {
  taskId: string;
  startedAt?: string;
  updatedBy: string;
}

export interface ResearchDirectionProgressUpdateInput {
  taskId: string;
  status: Extract<RequirementCoverageStatus, 'partial' | 'completed'>;
  completedContent?: string;
  remainingContent?: string;
  workStartedAt?: string;
  updatedAt?: string;
  updatedBy: string;
}

export type PlanLogDraft = Omit<VersionPlanLog, 'id' | 'createdAt'> & {
  id?: string;
  createdAt?: string;
};

export type VersionPlanLogView = VersionPlanLog & {
  planId: string;
  planTitle: string;
};

export const REQUIREMENT_COVERAGE_LABEL: Record<RequirementCoverageStatus, string> = {
  not_started: '未开始',
  partial: '部分完成',
  completed: '完全完成',
};

export function getResearchDirectionStatus(task: Pick<PlanTask, 'status'>): RequirementCoverageStatus {
  if (task.status === 'completed') return 'completed';
  if (task.status === 'in_progress') return 'partial';
  return 'not_started';
}

export function getResearchDirectionProgressSummary(plan: VersionPlan): {
  total: number;
  completed: number;
  partial: number;
  notStarted: number;
  percent: number;
} {
  const tasks = plan.tasks ?? [];
  const total = tasks.length;
  const completed = tasks.filter((task) => getResearchDirectionStatus(task) === 'completed').length;
  const partial = tasks.filter((task) => getResearchDirectionStatus(task) === 'partial').length;
  const notStarted = Math.max(total - completed - partial, 0);
  return {
    total,
    completed,
    partial,
    notStarted,
    percent: total === 0 ? 0 : Math.round((completed / total) * 100),
  };
}

export interface VersionPlan {
  id: string;
  versionId: string;
  type: 'research' | 'product' | 'ui';
  title: string;
  owner: string;
  startTime: string;
  endTime: string;
  status: 'pending' | 'in_progress' | 'completed';
  tasks?: PlanTask[];
  completedRequirementIds?: string[];
  linkedRequirementIds?: string[];
  requirementCoverage?: VersionPlanRequirementCoverage[];
  logs?: VersionPlanLog[];
  productPlanKind?: ProductPlanKind;
  resultType?: 'link' | 'file';
  resultTitle?: string;
  resultUrl?: string;
  resultFileName?: string;
  resultFileData?: string;
  prototypeReviewConfirmed?: boolean;
  reviewResult?: ProductPlanReviewResult;
  reviewFailureTypes?: ProductPlanReviewFailureType[];
  reviewFailureReason?: string;
  remark?: string;
  overdueReason?: string;
  actualStartAt?: string;
  createdAt: string;
  completedAt?: string;
  addedBy: string;
  aiDecomposeStatus?: 'idle' | 'in_progress' | 'completed' | 'error';
  aiDecomposeAt?: string;
  aiDecomposeBy?: string;
  aiDecomposeTarget?: AgentDecomposeTarget;
  aiDecomposeError?: string;
}

export type PlanType = VersionPlan['type'];

function makePlanLogId(createdAt: string): string {
  const time = new Date(createdAt).getTime();
  const suffix = Math.random().toString(36).slice(2, 8);
  return `plan-log-${Number.isFinite(time) ? time : Date.now()}-${suffix}`;
}

export function getRequirementCoverage(plan: VersionPlan, requirementId: string): VersionPlanRequirementCoverage | undefined {
  const explicit = plan.requirementCoverage?.find((item) => item.requirementId === requirementId);
  if (explicit) return explicit;
  if ((plan.completedRequirementIds ?? []).includes(requirementId)) {
    return {
      requirementId,
      status: 'completed',
      updatedAt: plan.completedAt ?? plan.createdAt,
      updatedBy: plan.owner,
    };
  }
  return undefined;
}

export function getRequirementCoverageStatus(plan: VersionPlan, requirementId: string): RequirementCoverageStatus {
  return getRequirementCoverage(plan, requirementId)?.status ?? 'not_started';
}

export function getRequirementCoverageSummary(plan: VersionPlan): {
  total: number;
  completed: number;
  partial: number;
  notStarted: number;
  percent: number;
} {
  const linkedIds = plan.linkedRequirementIds ?? [];
  const total = linkedIds.length;
  const completed = linkedIds.filter((id) => getRequirementCoverageStatus(plan, id) === 'completed').length;
  const partial = linkedIds.filter((id) => getRequirementCoverageStatus(plan, id) === 'partial').length;
  const notStarted = Math.max(total - completed - partial, 0);
  return {
    total,
    completed,
    partial,
    notStarted,
    percent: total === 0 ? 0 : Math.round((completed / total) * 100),
  };
}

export function canSaveRequirementCoverageDraft(
  status: RequirementCoverageStatus,
  completedContent?: string,
  remainingContent?: string,
  workStartedAt?: string,
): boolean {
  if (!workStartedAt?.trim()) return false;
  if (status === 'completed') return true;
  if (status === 'partial') {
    return Boolean(completedContent?.trim()) && Boolean(remainingContent?.trim());
  }
  return false;
}

export function canOpenRequirementCoverageRecord(
  status: RequirementCoverageStatus,
  canEdit: boolean,
): boolean {
  return canEdit && status !== 'completed';
}

export function appendPlanLog(plan: VersionPlan, draft: PlanLogDraft): VersionPlanLog[] {
  const createdAt = draft.createdAt ?? new Date().toISOString();
  const log: VersionPlanLog = {
    ...draft,
    id: draft.id ?? makePlanLogId(createdAt),
    createdAt,
  };
  return [log, ...(plan.logs ?? [])];
}

function getPlanLogCreatedAtTime(log: VersionPlanLog): number {
  const time = new Date(log.createdAt).getTime();
  return Number.isFinite(time) ? time : 0;
}

export function getPlanLogsForPlans(plans: VersionPlan[]): VersionPlanLogView[] {
  return plans
    .flatMap((plan) => (plan.logs ?? []).map((log) => ({
      ...log,
      planId: plan.id,
      planTitle: plan.title,
    })))
    .sort((a, b) => getPlanLogCreatedAtTime(b) - getPlanLogCreatedAtTime(a));
}

export function startRequirementCoverageWork(
  plan: VersionPlan,
  input: RequirementCoverageWorkStartInput,
): Pick<VersionPlan, 'requirementCoverage'> {
  const startedAt = input.startedAt ?? new Date().toISOString();
  const existing = getRequirementCoverage(plan, input.requirementId);
  const nextCoverage: VersionPlanRequirementCoverage = {
    requirementId: input.requirementId,
    status: existing?.status ?? 'not_started',
    completedContent: existing?.completedContent,
    remainingContent: existing?.remainingContent,
    currentWorkStartedAt: startedAt,
    updatedAt: startedAt,
    updatedBy: input.updatedBy,
  };

  return {
    requirementCoverage: [
      nextCoverage,
      ...(plan.requirementCoverage ?? []).filter((item) => item.requirementId !== input.requirementId),
    ],
  };
}

export function updateRequirementCoverage(
  plan: VersionPlan,
  input: RequirementCoverageUpdateInput,
): Pick<VersionPlan, 'requirementCoverage' | 'completedRequirementIds' | 'logs'> {
  const updatedAt = input.updatedAt ?? new Date().toISOString();
  const existingCoverage = plan.requirementCoverage?.find((item) => item.requirementId === input.requirementId);
  const workStartedAt = input.workStartedAt?.trim() || existingCoverage?.currentWorkStartedAt;
  const nextCoverage: VersionPlanRequirementCoverage = {
    requirementId: input.requirementId,
    status: input.status,
    completedContent: input.completedContent?.trim() || undefined,
    remainingContent: input.remainingContent?.trim() || undefined,
    updatedAt,
    updatedBy: input.updatedBy,
  };
  const requirementCoverage = [
    nextCoverage,
    ...(plan.requirementCoverage ?? []).filter((item) => item.requirementId !== input.requirementId),
  ];

  const completedSet = new Set(plan.completedRequirementIds ?? []);
  if (input.status === 'completed') completedSet.add(input.requirementId);
  else completedSet.delete(input.requirementId);

  const detail = [
    nextCoverage.completedContent ? `已完成：${nextCoverage.completedContent}` : '',
    nextCoverage.remainingContent ? `剩余：${nextCoverage.remainingContent}` : '',
  ].filter(Boolean).join('\n');
  const reqLabel = [input.requirementCode, input.requirementTitle].filter(Boolean).join(' ');

  return {
    requirementCoverage,
    completedRequirementIds: Array.from(completedSet),
    logs: appendPlanLog(plan, {
      type: 'requirement_progress',
      createdAt: updatedAt,
      actor: input.updatedBy,
      title: `${reqLabel || '需求'}更新为${REQUIREMENT_COVERAGE_LABEL[input.status]}`,
      detail: detail || undefined,
      requirementId: input.requirementId,
      requirementCode: input.requirementCode,
      requirementTitle: input.requirementTitle,
      coverageStatus: input.status,
      completedContent: nextCoverage.completedContent,
      remainingContent: nextCoverage.remainingContent,
      workStartedAt,
    }),
  };
}

export function startResearchDirectionWork(
  plan: VersionPlan,
  input: ResearchDirectionWorkStartInput,
): Pick<VersionPlan, 'tasks'> {
  const tasks = plan.tasks ?? [];
  const startedAt = input.startedAt ?? new Date().toISOString();
  const nextTasks = tasks.map((task) => {
    if (task.id !== input.taskId || task.status === 'completed') return task;
    return {
      ...task,
      status: 'in_progress' as const,
      currentWorkStartedAt: startedAt,
      updatedAt: startedAt,
      updatedBy: input.updatedBy,
    };
  });

  return { tasks: nextTasks };
}

export function updateResearchDirectionProgress(
  plan: VersionPlan,
  input: ResearchDirectionProgressUpdateInput,
): Pick<VersionPlan, 'tasks' | 'logs'> {
  const tasks = plan.tasks ?? [];
  const target = tasks.find((task) => task.id === input.taskId);
  if (!target) {
    return {
      tasks: plan.tasks,
      logs: plan.logs,
    };
  }

  const updatedAt = input.updatedAt ?? new Date().toISOString();
  const workStartedAt = input.workStartedAt?.trim() || target.currentWorkStartedAt;
  const nextStatus: PlanTaskStatus = input.status === 'completed' ? 'completed' : 'in_progress';
  const completedContent = input.status === 'partial' ? input.completedContent?.trim() || undefined : undefined;
  const remainingContent = input.status === 'partial' ? input.remainingContent?.trim() || undefined : undefined;
  const nextTasks = tasks.map((task) => task.id === input.taskId
    ? {
      ...task,
      status: nextStatus,
      completedContent,
      remainingContent,
      currentWorkStartedAt: undefined,
      updatedAt,
      updatedBy: input.updatedBy,
    }
    : task);
  const detail = [
    completedContent ? `已完成：${completedContent}` : '',
    remainingContent ? `剩余：${remainingContent}` : '',
  ].filter(Boolean).join('\n');

  return {
    tasks: nextTasks,
    logs: appendPlanLog(plan, {
      type: 'research_direction_progress',
      createdAt: updatedAt,
      actor: input.updatedBy,
      title: `${target.title}更新为${REQUIREMENT_COVERAGE_LABEL[input.status]}`,
      detail: detail || undefined,
      directionTaskId: target.id,
      directionTitle: target.title,
      coverageStatus: input.status,
      completedContent,
      remainingContent,
      workStartedAt,
    }),
  };
}

function getPlanCreatedAtTime(plan: VersionPlan): number {
  const time = new Date(plan.createdAt).getTime();
  return Number.isFinite(time) ? time : 0;
}

function getPlanIdTime(plan: VersionPlan): number {
  const match = /^plan-(\d+)$/.exec(plan.id);
  if (!match) return 0;
  const time = Number(match[1]);
  return Number.isFinite(time) ? time : 0;
}

export function sortPlansNewestFirst(plans: VersionPlan[]): VersionPlan[] {
  return [...plans].sort((a, b) => {
    const createdDiff = getPlanCreatedAtTime(b) - getPlanCreatedAtTime(a);
    if (createdDiff !== 0) return createdDiff;
    return getPlanIdTime(b) - getPlanIdTime(a);
  });
}

export function calcPlanProgress(tasks?: PlanTask[]): number {
  if (!tasks || tasks.length === 0) return 0;
  const completed = tasks.filter((t) => t.status === 'completed').length;
  return Math.round((completed / tasks.length) * 100);
}

export function calcLinkedReqProgress(linkedIds?: string[], completedIds?: string[]): number {
  if (!linkedIds || linkedIds.length === 0) return 0;
  const done = (completedIds || []).filter((id) => linkedIds.includes(id)).length;
  return Math.round((done / linkedIds.length) * 100);
}

export function calcPlanDuration(start: string, end: string): { days: number; hours: number } {
  const s = new Date(start).getTime();
  const e = new Date(end).getTime();
  if (isNaN(s) || isNaN(e) || e < s) return { days: 0, hours: 0 };
  const diffHours = (e - s) / (1000 * 60 * 60);
  const hours = Math.round(diffHours * 2) / 2; // 精确到0.5h
  const days = Math.ceil(diffHours / 24);
  return { days, hours };
}

export function formatDuration(days: number, hours: number): string {
  if (hours <= 0) return '0h';
  if (hours < 24) return `${hours}h`;
  if (hours % 24 === 0) return `${hours / 24}天`;
  return `${hours}h`;
}

export function calcTotalDuration(plans: VersionPlan[]): string {
  const today = new Date().toISOString().slice(0, 10);
  const activePlans = plans.filter((p) => p.status !== 'pending' || p.startTime <= today);
  if (activePlans.length === 0) return '0天';

  const intervals = activePlans.map((p) => {
    const start = new Date(p.startTime).getTime();
    const end = p.completedAt
      ? new Date(p.completedAt).getTime()
      : p.startTime <= today
        ? new Date(today).getTime()
        : new Date(p.startTime).getTime();
    return { start, end };
  }).filter((i) => i.end > i.start).sort((a, b) => a.start - b.start);

  if (intervals.length === 0) return '0天';

  let totalMs = 0;
  let currentStart = intervals[0].start;
  let currentEnd = intervals[0].end;

  for (let i = 1; i < intervals.length; i++) {
    if (intervals[i].start <= currentEnd) {
      currentEnd = Math.max(currentEnd, intervals[i].end);
    } else {
      totalMs += currentEnd - currentStart;
      currentStart = intervals[i].start;
      currentEnd = intervals[i].end;
    }
  }
  totalMs += currentEnd - currentStart;

  const totalDays = Math.ceil(totalMs / (1000 * 60 * 60 * 24));
  return `${totalDays}天`;
}

import type { TimeInterval } from './work-hours';

/**
 * 抽取每条已开始计划的 [actualStartAt, completedAt ?? now] 时间区间
 * 用于双口径耗时统计（calcTwoMetrics）
 */
export function planIntervals(plans: VersionPlan[], now: Date = new Date()): TimeInterval[] {
  const out: TimeInterval[] = [];
  const nowIso = now.toISOString();
  for (const p of plans) {
    if (!p.actualStartAt) continue;
    out.push({ start: p.actualStartAt, end: p.completedAt ?? nowIso });
  }
  return out;
}
