import type {
  PortfolioGanttTimeline,
  PortfolioHealthTrend,
  PortfolioTimelineRow,
} from '@/features/projects/types/project.types';

export type PortfolioHealthClass = 'excellent' | 'healthy' | 'at_risk' | 'critical';
export type ScheduleVarianceType = 'delayed' | 'on_schedule' | 'ahead';
export type AttentionSeverity = 'critical' | 'high' | 'medium';

export interface ScheduleVariance {
  type: ScheduleVarianceType;
  days: number;
}

export type AttentionCategory = 'critical' | 'blocked' | 'delayed' | 'at_risk' | 'upcoming';

export interface PortfolioAttentionItem {
  projectId: string;
  project: string;
  signal: string;
  priorityScore: number;
  category: AttentionCategory;
}

export type VarianceBadgeTone = 'ahead' | 'on_schedule' | 'late_mild' | 'late_severe';

export interface VarianceBadge {
  label: string;
  tone: VarianceBadgeTone;
}

export interface PortfolioHealthResult {
  score: number;
  classification: PortfolioHealthClass;
  trend: 'up' | 'down' | 'stable';
  previousScore: number | null;
  trendPercent: number | null;
}

export function resolvePortfolioHealthTrend(
  summary: PortfolioGanttTimeline['summary'],
  fallbackTrend: 'up' | 'down' | 'stable' = 'stable',
): Pick<PortfolioHealthResult, 'trend' | 'previousScore' | 'trendPercent'> {
  const apiTrend = summary.health_trend;
  if (apiTrend) {
    return {
      trend: apiTrend.direction,
      previousScore: apiTrend.previous_score,
      trendPercent: apiTrend.trend_percent,
    };
  }

  return {
    trend: fallbackTrend,
    previousScore: null,
    trendPercent: null,
  };
}

export function formatHealthTrendLabel(
  trend: PortfolioHealthTrend | undefined,
  t: (key: string, opts?: Record<string, unknown>) => string,
): string {
  if (!trend || trend.trend_percent === null || trend.previous_score === null) {
    return t('portfolioTimeline.trend.stable');
  }

  const sign = trend.direction === 'up' ? '↑' : trend.direction === 'down' ? '↓' : '→';
  const pct = Math.abs(trend.trend_percent);

  return t('portfolioTimeline.trend.vsLastMonth', {
    sign,
    percent: pct,
    defaultValue: `${sign} ${pct}% vs last month`,
  });
}

function parseDate(s: string | null | undefined): Date | null {
  if (!s) return null;
  const d = new Date(s);
  return Number.isNaN(d.getTime()) ? null : d;
}

function healthPoints(health: string): number {
  switch (health) {
    case 'healthy':
      return 100;
    case 'attention':
      return 80;
    case 'at_risk':
      return 60;
    case 'critical':
      return 30;
    default:
      return 70;
  }
}

export function classifyPortfolioHealth(score: number): PortfolioHealthClass {
  if (score >= 90) return 'excellent';
  if (score >= 75) return 'healthy';
  if (score >= 60) return 'at_risk';
  return 'critical';
}

export function computePortfolioHealthScore(
  rows: PortfolioTimelineRow[],
  summary: PortfolioGanttTimeline['summary'],
): PortfolioHealthResult {
  if (rows.length === 0) {
    return {
      score: 0,
      classification: 'critical',
      trend: 'stable',
      previousScore: null,
      trendPercent: null,
    };
  }

  const score =
    summary.portfolio_health_score ??
    (() => {
      const avgHealth =
        rows.reduce((sum, row) => sum + healthPoints(row.health), 0) / rows.length;
      const delayPenalty =
        summary.total_projects > 0
          ? (summary.delayed_projects / summary.total_projects) * 20
          : 0;
      const blockPenalty =
        summary.total_projects > 0
          ? (summary.blocked_projects / summary.total_projects) * 15
          : 0;
      const avgRisks =
        rows.reduce((sum, row) => sum + (row.open_risks_count ?? 0), 0) / rows.length;
      const riskPenalty = Math.min(avgRisks * 3, 15);

      return Math.round(
        Math.max(0, Math.min(100, avgHealth - delayPenalty - blockPenalty - riskPenalty)),
      );
    })();

  const classification = (summary.portfolio_health_class as PortfolioHealthClass | undefined) ??
    classifyPortfolioHealth(score);

  let fallbackTrend: PortfolioHealthResult['trend'] = 'stable';
  if (summary.delayed_projects + summary.blocked_projects > summary.total_projects * 0.25) {
    fallbackTrend = 'down';
  } else if (score >= 85 && summary.at_risk_projects === 0) {
    fallbackTrend = 'up';
  }

  const trendMeta = resolvePortfolioHealthTrend(summary, fallbackTrend);

  return {
    score,
    classification,
    ...trendMeta,
  };
}

export function computeOnTrack(summary: PortfolioGanttTimeline['summary']): number {
  return Math.max(
    0,
    summary.total_projects -
      summary.at_risk_projects -
      summary.delayed_projects -
      summary.blocked_projects -
      summary.completed_projects,
  );
}

export function computePortfolioProgress(rows: PortfolioTimelineRow[]): number {
  if (rows.length === 0) return 0;
  const total = rows.reduce((sum, row) => sum + (row.progress_percentage ?? 0), 0);
  return Math.round(total / rows.length);
}

export function daysDelayed(endDate: Date | null): number {
  if (!endDate) return 0;
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  const end = new Date(endDate);
  end.setHours(0, 0, 0, 0);
  if (end >= today) return 0;
  return Math.ceil((today.getTime() - end.getTime()) / 86400000);
}

export function computeScheduleVariance(row: PortfolioTimelineRow): ScheduleVariance {
  const start = parseDate(row.start_date ?? row.planned_start_date);
  const end = parseDate(row.end_date ?? row.planned_end_date);
  const today = new Date();
  today.setHours(0, 0, 0, 0);

  if (row.is_delayed || (end && end < today && row.status !== 'completed')) {
    return { type: 'delayed', days: daysDelayed(end) };
  }

  if (start && end && end > today) {
    const durationMs = Math.max(end.getTime() - start.getTime(), 1);
    const elapsedMs = Math.max(today.getTime() - start.getTime(), 0);
    const elapsedRatio = elapsedMs / durationMs;
    const progressRatio = (row.progress_percentage ?? 0) / 100;

    if (progressRatio > elapsedRatio + 0.08) {
      const durationDays = durationMs / 86400000;
      const aheadDays = Math.max(
        1,
        Math.round((progressRatio - elapsedRatio) * durationDays),
      );
      return { type: 'ahead', days: aheadDays };
    }
  }

  return { type: 'on_schedule', days: 0 };
}

export function formatVarianceBadge(
  variance: ScheduleVariance,
  t: (key: string, opts?: Record<string, unknown>) => string,
): VarianceBadge {
  if (variance.type === 'ahead') {
    return {
      label: t('portfolioTimeline.variance.aheadBadge', { count: variance.days }),
      tone: 'ahead',
    };
  }

  if (variance.type === 'delayed') {
    return {
      label: t('portfolioTimeline.variance.lateBadge', { count: variance.days }),
      tone: variance.days >= 7 ? 'late_severe' : 'late_mild',
    };
  }

  return {
    label: t('portfolioTimeline.variance.onScheduleBadge'),
    tone: 'on_schedule',
  };
}

export function computeHealthyProjectCount(rows: PortfolioTimelineRow[]): number {
  return rows.filter((row) => row.health === 'healthy').length;
}

function severityWeight(severity: AttentionSeverity): number {
  if (severity === 'critical') return 3;
  if (severity === 'high') return 2;
  return 1;
}

const CATEGORY_PRIORITY: Record<AttentionCategory, number> = {
  critical: 5,
  blocked: 4,
  delayed: 3,
  at_risk: 2,
  upcoming: 1,
};

function priorityScore(weight: number, bonus = 0): number {
  return Math.min(99, Math.max(50, Math.round(55 + weight * 12 + bonus)));
}

export function buildAttentionItems(
  rows: PortfolioTimelineRow[],
  labels: {
    delayed: (count: number) => string;
    blocked: string;
    atRisk: string;
    milestoneMissed: string;
    criticalDependency: string;
    risks: (count: number) => string;
  },
): PortfolioAttentionItem[] {
  const items: Array<PortfolioAttentionItem & { weight: number }> = [];

  for (const row of rows) {
    const variance = computeScheduleVariance(row);

    if (row.health === 'critical') {
      items.push({
        projectId: row.project_id,
        project: row.title,
        signal: labels.atRisk,
        category: 'critical',
        priorityScore: priorityScore(severityWeight('critical') + 2),
        weight: severityWeight('critical') + 3,
      });
      continue;
    }

    if (row.is_blocked || (row.blocked_dependencies_count ?? 0) > 0) {
      items.push({
        projectId: row.project_id,
        project: row.title,
        signal: labels.blocked,
        category: 'blocked',
        priorityScore: priorityScore(severityWeight('high') + 2, row.blocked_dependencies_count ?? 0),
        weight: severityWeight('high') + 2,
      });
      continue;
    }

    if (variance.type === 'delayed' && variance.days >= 7) {
      items.push({
        projectId: row.project_id,
        project: row.title,
        signal: labels.delayed(variance.days),
        category: 'delayed',
        priorityScore: priorityScore(severityWeight('critical') + 1, variance.days),
        weight: severityWeight('critical') + variance.days / 7,
      });
      continue;
    }

    if (variance.type === 'delayed') {
      items.push({
        projectId: row.project_id,
        project: row.title,
        signal: labels.delayed(variance.days),
        category: 'delayed',
        priorityScore: priorityScore(severityWeight('high'), variance.days),
        weight: severityWeight('high') + variance.days / 14,
      });
      continue;
    }

    if ((row.delayed_milestones_count ?? 0) > 0) {
      items.push({
        projectId: row.project_id,
        project: row.title,
        signal: labels.milestoneMissed,
        category: 'delayed',
        priorityScore: priorityScore(severityWeight('high') + 1),
        weight: severityWeight('high') + 1,
      });
      continue;
    }

    if ((row.open_coordination_dependencies_count ?? 0) > 0 && row.is_at_risk) {
      items.push({
        projectId: row.project_id,
        project: row.title,
        signal: labels.criticalDependency,
        category: 'critical',
        priorityScore: priorityScore(severityWeight('critical')),
        weight: severityWeight('critical'),
      });
      continue;
    }

    if (
      row.is_at_risk ||
      row.health === 'at_risk' ||
      row.health === 'attention'
    ) {
      items.push({
        projectId: row.project_id,
        project: row.title,
        signal: labels.atRisk,
        category: 'at_risk',
        priorityScore: priorityScore(severityWeight('high')),
        weight: severityWeight('high'),
      });
      continue;
    }

    if ((row.open_risks_count ?? 0) >= 2) {
      items.push({
        projectId: row.project_id,
        project: row.title,
        signal: labels.risks(row.open_risks_count ?? 0),
        category: 'upcoming',
        priorityScore: priorityScore(severityWeight('medium'), row.open_risks_count ?? 0),
        weight: severityWeight('medium') + (row.open_risks_count ?? 0) * 0.2,
      });
    }
  }

  return items
    .sort((a, b) => {
      const categoryDelta = CATEGORY_PRIORITY[b.category] - CATEGORY_PRIORITY[a.category];
      if (categoryDelta !== 0) return categoryDelta;
      return b.priorityScore - a.priorityScore;
    })
    .slice(0, 5)
    .map(({ weight: _weight, ...item }) => item);
}

export function formatForecastKpi(
  forecast: PortfolioGanttTimeline['summary']['portfolio_forecast'],
  t: (key: string, opts?: Record<string, unknown>) => string,
): { primary: string; secondary: string } {
  if (!forecast?.forecast_finish) {
    return {
      primary: t('portfolioTimeline.forecast.unavailable'),
      secondary: '',
    };
  }

  const date = new Date(forecast.forecast_finish);
  const formatted = date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });

  if (forecast.variance_days !== null && forecast.variance_days > 0) {
    return {
      primary: t('portfolioTimeline.forecast.behindPlan', { days: forecast.variance_days }),
      secondary: t('portfolioTimeline.forecast.confidence', { percent: forecast.confidence_percent }),
    };
  }

  return {
    primary: formatted,
    secondary: t('portfolioTimeline.forecast.confidence', { percent: forecast.confidence_percent }),
  };
}

export function mapPortfolioHealthToExecutive(
  health: string,
): 'good' | 'warning' | 'at_risk' | 'critical' {
  if (health === 'healthy') return 'good';
  if (health === 'attention') return 'warning';
  if (health === 'at_risk') return 'at_risk';
  if (health === 'critical') return 'critical';
  return 'warning';
}

export function blockedTrend(summary: PortfolioGanttTimeline['summary']): 'up' | 'down' | 'stable' {
  if (summary.blocked_projects === 0) return 'stable';
  if (summary.blocked_projects >= Math.max(2, summary.total_projects * 0.2)) return 'up';
  return 'stable';
}
