/**
 * Portfolio Service
 * Manages user portfolios, allocations, and rebalancing
 */

import { v4 as uuidv4 } from 'uuid';
import { accountService } from '../../investment/services/account.service';
import { marketService } from '../../trading/services/market.service';

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

export type RiskProfile = 'conservative' | 'moderate' | 'aggressive';

export interface Portfolio {
  id: string;
  userId: string;
  name: string;
  riskProfile: RiskProfile;
  allocations: PortfolioAllocation[];
  totalValue: number;
  totalCost: number;
  unrealizedPnl: number;
  unrealizedPnlPercent: number;
  realizedPnl: number;
  lastRebalanced: Date | null;
  createdAt: Date;
  updatedAt: Date;
}

export interface PortfolioAllocation {
  id: string;
  portfolioId: string;
  asset: string;
  targetPercent: number;
  currentPercent: number;
  quantity: number;
  value: number;
  cost: number;
  pnl: number;
  pnlPercent: number;
  deviation: number;
}

export interface PortfolioGoal {
  id: string;
  userId: string;
  name: string;
  targetAmount: number;
  currentAmount: number;
  targetDate: Date;
  monthlyContribution: number;
  progress: number;
  projectedCompletion: Date | null;
  status: 'on_track' | 'at_risk' | 'behind';
  createdAt: Date;
  updatedAt: Date;
}

export interface RebalanceRecommendation {
  asset: string;
  currentPercent: number;
  targetPercent: number;
  action: 'buy' | 'sell' | 'hold';
  amount: number;
  amountUSD: number;
  priority: 'high' | 'medium' | 'low';
}

export interface PortfolioStats {
  totalValue: number;
  dayChange: number;
  dayChangePercent: number;
  weekChange: number;
  weekChangePercent: number;
  monthChange: number;
  monthChangePercent: number;
  allTimeChange: number;
  allTimeChangePercent: number;
  bestPerformer: { asset: string; change: number };
  worstPerformer: { asset: string; change: number };
}

// ============================================================================
// Default Allocations by Risk Profile
// ============================================================================

const DEFAULT_ALLOCATIONS: Record<RiskProfile, { asset: string; percent: number }[]> = {
  conservative: [
    { asset: 'USDT', percent: 50 },
    { asset: 'BTC', percent: 30 },
    { asset: 'ETH', percent: 20 },
  ],
  moderate: [
    { asset: 'USDT', percent: 20 },
    { asset: 'BTC', percent: 40 },
    { asset: 'ETH', percent: 25 },
    { asset: 'SOL', percent: 10 },
    { asset: 'LINK', percent: 5 },
  ],
  aggressive: [
    { asset: 'USDT', percent: 10 },
    { asset: 'BTC', percent: 30 },
    { asset: 'ETH', percent: 25 },
    { asset: 'SOL', percent: 15 },
    { asset: 'LINK', percent: 10 },
    { asset: 'AVAX', percent: 10 },
  ],
};

// ============================================================================
// In-Memory Storage
// ============================================================================

const portfolios: Map<string, Portfolio> = new Map();
const goals: Map<string, PortfolioGoal> = new Map();

// ============================================================================
// Portfolio Service
// ============================================================================

class PortfolioService {
  // ==========================================================================
  // Portfolio Management
  // ==========================================================================

  /**
   * Create a new portfolio
   */
  async createPortfolio(
    userId: string,
    name: string,
    riskProfile: RiskProfile,
    initialValue: number = 0
  ): Promise<Portfolio> {
    const defaultAllocations = DEFAULT_ALLOCATIONS[riskProfile];

    const portfolio: Portfolio = {
      id: uuidv4(),
      userId,
      name,
      riskProfile,
      allocations: defaultAllocations.map((a) => ({
        id: uuidv4(),
        portfolioId: '',
        asset: a.asset,
        targetPercent: a.percent,
        currentPercent: a.percent,
        quantity: 0,
        value: (initialValue * a.percent) / 100,
        cost: (initialValue * a.percent) / 100,
        pnl: 0,
        pnlPercent: 0,
        deviation: 0,
      })),
      totalValue: initialValue,
      totalCost: initialValue,
      unrealizedPnl: 0,
      unrealizedPnlPercent: 0,
      realizedPnl: 0,
      lastRebalanced: null,
      createdAt: new Date(),
      updatedAt: new Date(),
    };

    // Set portfolio ID in allocations
    portfolio.allocations.forEach((a) => {
      a.portfolioId = portfolio.id;
    });

    portfolios.set(portfolio.id, portfolio);
    return portfolio;
  }

  /**
   * Get portfolio by ID
   */
  async getPortfolio(portfolioId: string): Promise<Portfolio | null> {
    const portfolio = portfolios.get(portfolioId);
    if (!portfolio) return null;

    // Update current values
    await this.updatePortfolioValues(portfolio);

    return portfolio;
  }

  /**
   * Get user portfolios
   */
  async getUserPortfolios(userId: string): Promise<Portfolio[]> {
    const userPortfolios = Array.from(portfolios.values())
      .filter((p) => p.userId === userId);

    // Update values for all portfolios
    await Promise.all(userPortfolios.map((p) => this.updatePortfolioValues(p)));

    return userPortfolios;
  }

  /**
   * Update portfolio allocations
   */
  async updateAllocations(
    portfolioId: string,
    allocations: { asset: string; targetPercent: number }[]
  ): Promise<Portfolio> {
    const portfolio = portfolios.get(portfolioId);
    if (!portfolio) {
      throw new Error(`Portfolio not found: ${portfolioId}`);
    }

    // Validate total is 100%
    const total = allocations.reduce((sum, a) => sum + a.targetPercent, 0);
    if (Math.abs(total - 100) > 0.01) {
      throw new Error('Allocations must sum to 100%');
    }

    // Update allocations
    portfolio.allocations = allocations.map((a) => {
      const existing = portfolio.allocations.find((e) => e.asset === a.asset);
      return {
        id: existing?.id || uuidv4(),
        portfolioId,
        asset: a.asset,
        targetPercent: a.targetPercent,
        currentPercent: existing?.currentPercent || 0,
        quantity: existing?.quantity || 0,
        value: existing?.value || 0,
        cost: existing?.cost || 0,
        pnl: existing?.pnl || 0,
        pnlPercent: existing?.pnlPercent || 0,
        deviation: 0,
      };
    });

    portfolio.updatedAt = new Date();
    await this.updatePortfolioValues(portfolio);

    return portfolio;
  }

  /**
   * Get rebalancing recommendations
   */
  async getRebalanceRecommendations(portfolioId: string): Promise<RebalanceRecommendation[]> {
    const portfolio = await this.getPortfolio(portfolioId);
    if (!portfolio) {
      throw new Error(`Portfolio not found: ${portfolioId}`);
    }

    const recommendations: RebalanceRecommendation[] = [];
    const rebalanceThreshold = 5; // 5% deviation triggers rebalance

    for (const allocation of portfolio.allocations) {
      const deviation = allocation.currentPercent - allocation.targetPercent;
      const absDeviation = Math.abs(deviation);

      if (absDeviation < rebalanceThreshold) {
        recommendations.push({
          asset: allocation.asset,
          currentPercent: allocation.currentPercent,
          targetPercent: allocation.targetPercent,
          action: 'hold',
          amount: 0,
          amountUSD: 0,
          priority: 'low',
        });
        continue;
      }

      const targetValue = (portfolio.totalValue * allocation.targetPercent) / 100;
      const difference = targetValue - allocation.value;

      recommendations.push({
        asset: allocation.asset,
        currentPercent: allocation.currentPercent,
        targetPercent: allocation.targetPercent,
        action: difference > 0 ? 'buy' : 'sell',
        amount: Math.abs(difference / (allocation.value / allocation.quantity || 1)),
        amountUSD: Math.abs(difference),
        priority: absDeviation > 10 ? 'high' : 'medium',
      });
    }

    // Sort by priority and absolute deviation
    return recommendations.sort((a, b) => {
      const priorityOrder = { high: 0, medium: 1, low: 2 };
      return priorityOrder[a.priority] - priorityOrder[b.priority];
    });
  }

  /**
   * Execute rebalancing
   */
  async executeRebalance(portfolioId: string): Promise<Portfolio> {
    const portfolio = await this.getPortfolio(portfolioId);
    if (!portfolio) {
      throw new Error(`Portfolio not found: ${portfolioId}`);
    }

    // Simulate rebalancing by adjusting allocations to target
    for (const allocation of portfolio.allocations) {
      allocation.currentPercent = allocation.targetPercent;
      allocation.deviation = 0;
    }

    portfolio.lastRebalanced = new Date();
    portfolio.updatedAt = new Date();

    return portfolio;
  }

  /**
   * Get portfolio statistics
   */
  async getPortfolioStats(portfolioId: string): Promise<PortfolioStats> {
    const portfolio = await this.getPortfolio(portfolioId);
    if (!portfolio) {
      throw new Error(`Portfolio not found: ${portfolioId}`);
    }

    // Calculate performance (mock data for now)
    const dayChange = portfolio.totalValue * 0.02; // 2% daily change
    const weekChange = portfolio.totalValue * 0.05;
    const monthChange = portfolio.totalValue * 0.12;

    // Find best and worst performers
    const sortedAllocations = [...portfolio.allocations].sort(
      (a, b) => b.pnlPercent - a.pnlPercent
    );

    return {
      totalValue: portfolio.totalValue,
      dayChange,
      dayChangePercent: (dayChange / portfolio.totalValue) * 100,
      weekChange,
      weekChangePercent: (weekChange / portfolio.totalValue) * 100,
      monthChange,
      monthChangePercent: (monthChange / portfolio.totalValue) * 100,
      allTimeChange: portfolio.unrealizedPnl + portfolio.realizedPnl,
      allTimeChangePercent: portfolio.unrealizedPnlPercent,
      bestPerformer: {
        asset: sortedAllocations[0]?.asset || 'N/A',
        change: sortedAllocations[0]?.pnlPercent || 0,
      },
      worstPerformer: {
        asset: sortedAllocations[sortedAllocations.length - 1]?.asset || 'N/A',
        change: sortedAllocations[sortedAllocations.length - 1]?.pnlPercent || 0,
      },
    };
  }

  // ==========================================================================
  // Goal Management
  // ==========================================================================

  /**
   * Create a financial goal
   */
  async createGoal(
    userId: string,
    name: string,
    targetAmount: number,
    targetDate: Date,
    monthlyContribution: number
  ): Promise<PortfolioGoal> {
    const goal: PortfolioGoal = {
      id: uuidv4(),
      userId,
      name,
      targetAmount,
      currentAmount: 0,
      targetDate,
      monthlyContribution,
      progress: 0,
      projectedCompletion: null,
      status: 'on_track',
      createdAt: new Date(),
      updatedAt: new Date(),
    };

    this.updateGoalProjection(goal);
    goals.set(goal.id, goal);

    return goal;
  }

  /**
   * Get user goals
   */
  async getUserGoals(userId: string): Promise<PortfolioGoal[]> {
    return Array.from(goals.values())
      .filter((g) => g.userId === userId)
      .map((g) => {
        this.updateGoalProjection(g);
        return g;
      });
  }

  /**
   * Update goal progress
   */
  async updateGoalProgress(
    goalId: string,
    currentAmount: number
  ): Promise<PortfolioGoal> {
    const goal = goals.get(goalId);
    if (!goal) {
      throw new Error(`Goal not found: ${goalId}`);
    }

    goal.currentAmount = currentAmount;
    goal.progress = (currentAmount / goal.targetAmount) * 100;
    goal.updatedAt = new Date();

    this.updateGoalProjection(goal);

    return goal;
  }

  /**
   * Delete a goal
   */
  async deleteGoal(goalId: string): Promise<boolean> {
    return goals.delete(goalId);
  }

  // ==========================================================================
  // Private Methods
  // ==========================================================================

  private async updatePortfolioValues(portfolio: Portfolio): Promise<void> {
    let totalValue = 0;

    for (const allocation of portfolio.allocations) {
      if (allocation.asset === 'USDT') {
        // Stablecoin, value = quantity
        allocation.value = allocation.quantity;
      } else {
        try {
          const price = await marketService.getPrice(`${allocation.asset}USDT`);
          allocation.value = allocation.quantity * price.price;
        } catch {
          // Keep existing value if price fetch fails
        }
      }

      allocation.pnl = allocation.value - allocation.cost;
      allocation.pnlPercent =
        allocation.cost > 0 ? (allocation.pnl / allocation.cost) * 100 : 0;

      totalValue += allocation.value;
    }

    portfolio.totalValue = totalValue;
    portfolio.unrealizedPnl = totalValue - portfolio.totalCost;
    portfolio.unrealizedPnlPercent =
      portfolio.totalCost > 0
        ? (portfolio.unrealizedPnl / portfolio.totalCost) * 100
        : 0;

    // Update current percentages and deviations
    for (const allocation of portfolio.allocations) {
      allocation.currentPercent =
        totalValue > 0 ? (allocation.value / totalValue) * 100 : 0;
      allocation.deviation = allocation.currentPercent - allocation.targetPercent;
    }

    portfolio.updatedAt = new Date();
  }

  private updateGoalProjection(goal: PortfolioGoal): void {
    const monthsRemaining = Math.max(
      0,
      (goal.targetDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24 * 30)
    );

    const amountNeeded = goal.targetAmount - goal.currentAmount;
    const projectedFinalAmount =
      goal.currentAmount + goal.monthlyContribution * monthsRemaining;

    if (projectedFinalAmount >= goal.targetAmount) {
      goal.status = 'on_track';
      // Calculate when goal will be reached
      const monthsToGoal = amountNeeded / goal.monthlyContribution;
      goal.projectedCompletion = new Date(
        Date.now() + monthsToGoal * 30 * 24 * 60 * 60 * 1000
      );
    } else if (projectedFinalAmount >= goal.targetAmount * 0.8) {
      goal.status = 'at_risk';
      goal.projectedCompletion = null;
    } else {
      goal.status = 'behind';
      goal.projectedCompletion = null;
    }
  }
}

// Export singleton instance
export const portfolioService = new PortfolioService();
