/**
 * useOptimization Hook
 * Handles portfolio optimization calculations using Modern Portfolio Theory
 */

import { useState, useCallback, useMemo } from 'react';
import { useQuery, useMutation } from '@tanstack/react-query';

// ============================================================================
// Types
// ============================================================================

export interface OptimizationConstraints {
  riskTolerance: number; // 0-100 scale
  minWeight: number; // Minimum weight per asset (0-1)
  maxWeight: number; // Maximum weight per asset (0-1)
  targetReturn?: number; // Optional target annual return
  excludeAssets?: string[]; // Assets to exclude
  includeStablecoins?: boolean; // Include stablecoins in optimization
  maxAssets?: number; // Maximum number of assets
}

export interface OptimalAllocation {
  asset: string;
  weight: number;
  expectedReturn: number;
  volatility: number;
}

export interface EfficientFrontierPoint {
  return: number;
  risk: number;
  sharpeRatio: number;
  allocations: OptimalAllocation[];
}

export interface OptimizationResult {
  optimalPortfolio: OptimalAllocation[];
  expectedReturn: number;
  expectedVolatility: number;
  sharpeRatio: number;
  efficientFrontier: EfficientFrontierPoint[];
  currentComparison: {
    currentReturn: number;
    currentVolatility: number;
    currentSharpe: number;
    improvementReturn: number;
    improvementRisk: number;
    improvementSharpe: number;
  };
  optimizedAt: string;
}

export interface UseOptimizationOptions {
  onOptimizeSuccess?: (result: OptimizationResult) => void;
  onOptimizeError?: (error: Error) => void;
}

// ============================================================================
// API Functions
// ============================================================================

const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3080';

async function runOptimization(
  portfolioId: string,
  constraints: OptimizationConstraints
): Promise<OptimizationResult> {
  const response = await fetch(
    `${API_URL}/api/v1/portfolio/${portfolioId}/optimize`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      credentials: 'include',
      body: JSON.stringify(constraints),
    }
  );

  if (!response.ok) {
    throw new Error('Failed to run portfolio optimization');
  }

  const data = await response.json();
  return data.data || data;
}

async function getLastOptimization(
  portfolioId: string
): Promise<OptimizationResult | null> {
  const response = await fetch(
    `${API_URL}/api/v1/portfolio/${portfolioId}/optimize/latest`,
    { credentials: 'include' }
  );

  if (!response.ok) {
    if (response.status === 404) return null;
    throw new Error('Failed to fetch last optimization');
  }

  const data = await response.json();
  return data.data || data;
}

async function getEfficientFrontier(
  portfolioId: string,
  points: number = 20
): Promise<EfficientFrontierPoint[]> {
  const response = await fetch(
    `${API_URL}/api/v1/portfolio/${portfolioId}/efficient-frontier?points=${points}`,
    { credentials: 'include' }
  );

  if (!response.ok) {
    throw new Error('Failed to fetch efficient frontier');
  }

  const data = await response.json();
  return data.data || data;
}

// ============================================================================
// Local Optimization (Simplified Mean-Variance)
// ============================================================================

interface AssetData {
  asset: string;
  expectedReturn: number;
  volatility: number;
  weight: number;
}

function generateLocalOptimization(
  currentAllocations: AssetData[],
  constraints: OptimizationConstraints
): OptimizationResult {
  const { riskTolerance, minWeight, maxWeight, excludeAssets = [] } = constraints;

  // Filter out excluded assets
  const filteredAssets = currentAllocations.filter(
    (a) => !excludeAssets.includes(a.asset)
  );

  // Simple optimization based on risk-adjusted returns
  const riskFactor = riskTolerance / 100;

  // Score each asset: higher expected return and lower volatility is better
  const scoredAssets = filteredAssets.map((asset) => ({
    ...asset,
    score:
      asset.expectedReturn * (0.5 + riskFactor * 0.5) -
      asset.volatility * (1 - riskFactor) * 0.5,
  }));

  // Sort by score
  scoredAssets.sort((a, b) => b.score - a.score);

  // Distribute weights
  const totalScore = scoredAssets.reduce((sum, a) => sum + Math.max(0, a.score), 0);
  let optimalAllocations: OptimalAllocation[] = scoredAssets.map((asset) => {
    const rawWeight = totalScore > 0 ? Math.max(0, asset.score) / totalScore : 1 / scoredAssets.length;
    const weight = Math.max(minWeight, Math.min(maxWeight, rawWeight));
    return {
      asset: asset.asset,
      weight,
      expectedReturn: asset.expectedReturn,
      volatility: asset.volatility,
    };
  });

  // Normalize weights to sum to 1
  const totalWeight = optimalAllocations.reduce((sum, a) => sum + a.weight, 0);
  optimalAllocations = optimalAllocations.map((a) => ({
    ...a,
    weight: a.weight / totalWeight,
  }));

  // Calculate portfolio metrics
  const expectedReturn = optimalAllocations.reduce(
    (sum, a) => sum + a.weight * a.expectedReturn,
    0
  );

  // Simplified volatility (ignoring correlations)
  const expectedVolatility = Math.sqrt(
    optimalAllocations.reduce(
      (sum, a) => sum + Math.pow(a.weight * a.volatility, 2),
      0
    )
  );

  const riskFreeRate = 0.05; // 5% risk-free rate
  const sharpeRatio =
    expectedVolatility > 0
      ? (expectedReturn - riskFreeRate) / expectedVolatility
      : 0;

  // Calculate current portfolio metrics
  const currentReturn = currentAllocations.reduce(
    (sum, a) => sum + a.weight * a.expectedReturn,
    0
  );
  const currentVolatility = Math.sqrt(
    currentAllocations.reduce(
      (sum, a) => sum + Math.pow(a.weight * a.volatility, 2),
      0
    )
  );
  const currentSharpe =
    currentVolatility > 0
      ? (currentReturn - riskFreeRate) / currentVolatility
      : 0;

  // Generate simplified efficient frontier
  const efficientFrontier: EfficientFrontierPoint[] = [];
  for (let i = 0; i <= 10; i++) {
    const targetRisk = (i / 10) * 0.4; // 0% to 40% volatility
    const point: EfficientFrontierPoint = {
      return: riskFreeRate + targetRisk * 0.5, // Simplified return/risk relationship
      risk: targetRisk,
      sharpeRatio: targetRisk > 0 ? (riskFreeRate + targetRisk * 0.5 - riskFreeRate) / targetRisk : 0,
      allocations: optimalAllocations.map((a) => ({ ...a })),
    };
    efficientFrontier.push(point);
  }

  return {
    optimalPortfolio: optimalAllocations,
    expectedReturn,
    expectedVolatility,
    sharpeRatio,
    efficientFrontier,
    currentComparison: {
      currentReturn,
      currentVolatility,
      currentSharpe,
      improvementReturn: expectedReturn - currentReturn,
      improvementRisk: currentVolatility - expectedVolatility,
      improvementSharpe: sharpeRatio - currentSharpe,
    },
    optimizedAt: new Date().toISOString(),
  };
}

// ============================================================================
// Hook
// ============================================================================

export function useOptimization(
  portfolioId: string,
  options: UseOptimizationOptions = {}
) {
  const [localResult, setLocalResult] = useState<OptimizationResult | null>(null);
  const [constraints, setConstraints] = useState<OptimizationConstraints>({
    riskTolerance: 50,
    minWeight: 0.02,
    maxWeight: 0.4,
    includeStablecoins: true,
  });

  // Fetch last optimization
  const lastOptimizationQuery = useQuery({
    queryKey: ['optimization', portfolioId, 'latest'],
    queryFn: () => getLastOptimization(portfolioId),
    enabled: !!portfolioId,
    staleTime: 10 * 60 * 1000, // 10 minutes
  });

  // Fetch efficient frontier
  const efficientFrontierQuery = useQuery({
    queryKey: ['optimization', portfolioId, 'frontier'],
    queryFn: () => getEfficientFrontier(portfolioId),
    enabled: !!portfolioId,
    staleTime: 30 * 60 * 1000, // 30 minutes
  });

  // Run optimization mutation
  const optimizationMutation = useMutation({
    mutationFn: (params: OptimizationConstraints) =>
      runOptimization(portfolioId, params),
    onSuccess: (result) => {
      setLocalResult(result);
      options.onOptimizeSuccess?.(result);
    },
    onError: (error: Error) => {
      options.onOptimizeError?.(error);
    },
  });

  // Run local optimization (for faster response)
  const runLocalOptimization = useCallback(
    (currentAllocations: AssetData[]): OptimizationResult => {
      const result = generateLocalOptimization(currentAllocations, constraints);
      setLocalResult(result);
      return result;
    },
    [constraints]
  );

  // Update constraints
  const updateConstraints = useCallback(
    (updates: Partial<OptimizationConstraints>) => {
      setConstraints((prev) => ({ ...prev, ...updates }));
    },
    []
  );

  // Get active result (local or from API)
  const result = useMemo(
    () => localResult || lastOptimizationQuery.data,
    [localResult, lastOptimizationQuery.data]
  );

  // Calculate weight differences between current and optimal
  const weightDifferences = useCallback(
    (currentAllocations: { asset: string; weight: number }[]) => {
      if (!result?.optimalPortfolio) return [];

      return result.optimalPortfolio.map((optimal) => {
        const current = currentAllocations.find((c) => c.asset === optimal.asset);
        return {
          asset: optimal.asset,
          currentWeight: current?.weight || 0,
          optimalWeight: optimal.weight,
          difference: optimal.weight - (current?.weight || 0),
        };
      });
    },
    [result]
  );

  return {
    // Data
    result,
    lastOptimization: lastOptimizationQuery.data,
    efficientFrontier: efficientFrontierQuery.data || result?.efficientFrontier || [],
    constraints,

    // Computed
    weightDifferences,

    // Loading states
    isLoading: lastOptimizationQuery.isLoading,
    isOptimizing: optimizationMutation.isPending,
    isLoadingFrontier: efficientFrontierQuery.isLoading,

    // Error states
    error: lastOptimizationQuery.error || efficientFrontierQuery.error,
    optimizationError: optimizationMutation.error,

    // Actions
    optimize: optimizationMutation.mutate,
    optimizeAsync: optimizationMutation.mutateAsync,
    runLocalOptimization,
    updateConstraints,
    clearResult: () => setLocalResult(null),

    // Refetch
    refetch: lastOptimizationQuery.refetch,
    refetchFrontier: efficientFrontierQuery.refetch,
  };
}

export default useOptimization;
