"""
Trading Journal API
Handles daily/weekly plans, manual trade logging, journal entries, and decision logging
"""

from fastapi import APIRouter, HTTPException, Depends, UploadFile, File
from sqlalchemy.orm import Session
from sqlalchemy import and_, desc
from typing import List, Optional
from datetime import date, datetime, timedelta
from pydantic import BaseModel
import os
import shutil
import uuid

from app.db.database import get_db
from app.models.models import (
    TradingPlan,
    ManualTrade,
    JournalEntry,
    DecisionLog,
    WeeklyPlan,
    TradeAction
)

router = APIRouter(prefix="/api/journal", tags=["Trading Journal"])


# Pydantic Schemas

class TradingPlanCreate(BaseModel):
    plan_date: date
    plan_type: str = "daily"
    market_bias: str
    daily_target: Optional[float] = None
    max_loss: Optional[float] = None
    entry_zone_min: Optional[float] = None
    entry_zone_max: Optional[float] = None
    target_price: Optional[float] = None
    stop_loss: Optional[float] = None
    support_levels: List[float] = []
    resistance_levels: List[float] = []
    trading_notes: Optional[str] = None
    max_trades: int = 3
    ai_generated: bool = False
    ai_confidence: Optional[float] = None
    context_metrics: Optional[dict] = None


class TradingPlanUpdate(BaseModel):
    market_bias: Optional[str] = None
    daily_target: Optional[float] = None
    max_loss: Optional[float] = None
    entry_zone_min: Optional[float] = None
    entry_zone_max: Optional[float] = None
    target_price: Optional[float] = None
    stop_loss: Optional[float] = None
    support_levels: Optional[List[float]] = None
    resistance_levels: Optional[List[float]] = None
    trading_notes: Optional[str] = None
    max_trades: Optional[int] = None
    actual_trades: Optional[int] = None
    actual_pnl: Optional[float] = None
    plan_followed: Optional[bool] = None


class ManualTradeCreate(BaseModel):
    plan_id: Optional[int] = None
    symbol: str = "XAUUSD"
    action: str  # BUY or SELL
    entry_price: float
    exit_price: Optional[float] = None
    quantity: float
    broker: Optional[str] = None
    pnl: Optional[float] = None
    pnl_percent: Optional[float] = None
    notes: Optional[str] = None
    followed_plan: bool = True
    entry_time: Optional[datetime] = None
    exit_time: Optional[datetime] = None


class ManualTradeUpdate(BaseModel):
    exit_price: Optional[float] = None
    pnl: Optional[float] = None
    pnl_percent: Optional[float] = None
    notes: Optional[str] = None
    exit_time: Optional[datetime] = None


class JournalEntryCreate(BaseModel):
    entry_date: date
    mood: Optional[str] = None
    energy_level: Optional[int] = None
    stress_level: Optional[int] = None
    lessons_learned: Optional[str] = None
    what_went_well: Optional[str] = None
    what_to_improve: Optional[str] = None
    tomorrow_focus: Optional[str] = None
    mistakes_made: Optional[str] = None
    market_conditions: Optional[str] = None
    market_notes: Optional[str] = None


class DecisionLogCreate(BaseModel):
    ai_recommendation: Optional[str] = None
    ai_confidence: Optional[float] = None
    ai_reasoning: Optional[str] = None
    trader_action: Optional[str] = None
    trade_id: Optional[int] = None
    outcome: Optional[str] = None
    outcome_pnl: Optional[float] = None
    notes: Optional[str] = None


class WeeklyPlanCreate(BaseModel):
    week_start_date: date
    year: int
    week_number: int
    market_outlook: Optional[str] = None
    key_events: List[dict] = []
    major_levels: List[float] = []
    weekly_target: Optional[float] = None
    max_weekly_loss: Optional[float] = None
    target_trade_count: Optional[int] = None
    primary_strategy: Optional[str] = None
    focus_areas: Optional[str] = None
    risks_to_watch: Optional[str] = None


# Trading Plans Endpoints

@router.post("/plans", status_code=201)
async def create_trading_plan(
    plan: TradingPlanCreate,
    db: Session = Depends(get_db)
):
    """Create a new daily/weekly trading plan"""
    db_plan = TradingPlan(**plan.dict())
    db.add(db_plan)
    db.commit()
    db.refresh(db_plan)
    return db_plan


@router.get("/plans/today")
async def get_today_plan(db: Session = Depends(get_db)):
    """Get today's trading plan"""
    today = date.today()
    plan = db.query(TradingPlan).filter(
        and_(
            TradingPlan.plan_date == today,
            TradingPlan.plan_type == "daily"
        )
    ).first()

    if not plan:
        raise HTTPException(status_code=404, detail="No plan found for today")

    return plan


@router.get("/plans/date/{plan_date}")
async def get_plan_by_date(
    plan_date: date,
    db: Session = Depends(get_db)
):
    """Get trading plan for a specific date"""
    plan = db.query(TradingPlan).filter(
        TradingPlan.plan_date == plan_date
    ).first()

    if not plan:
        raise HTTPException(status_code=404, detail=f"No plan found for {plan_date}")

    return plan


@router.get("/plans")
async def get_plans(
    limit: int = 30,
    offset: int = 0,
    db: Session = Depends(get_db)
):
    """Get recent trading plans"""
    plans = db.query(TradingPlan).order_by(
        desc(TradingPlan.plan_date)
    ).limit(limit).offset(offset).all()

    return {"plans": plans, "total": db.query(TradingPlan).count()}


@router.put("/plans/{plan_id}")
async def update_trading_plan(
    plan_id: int,
    plan_update: TradingPlanUpdate,
    db: Session = Depends(get_db)
):
    """Update an existing trading plan"""
    db_plan = db.query(TradingPlan).filter(TradingPlan.id == plan_id).first()

    if not db_plan:
        raise HTTPException(status_code=404, detail="Plan not found")

    update_data = plan_update.dict(exclude_unset=True)
    for key, value in update_data.items():
        setattr(db_plan, key, value)

    db.commit()
    db.refresh(db_plan)
    return db_plan


@router.delete("/plans/{plan_id}")
async def delete_trading_plan(
    plan_id: int,
    db: Session = Depends(get_db)
):
    """Delete a trading plan"""
    db_plan = db.query(TradingPlan).filter(TradingPlan.id == plan_id).first()

    if not db_plan:
        raise HTTPException(status_code=404, detail="Plan not found")

    db.delete(db_plan)
    db.commit()
    return {"message": "Plan deleted successfully"}


# Manual Trades Endpoints

@router.post("/trades", status_code=201)
async def create_manual_trade(
    trade: ManualTradeCreate,
    db: Session = Depends(get_db)
):
    """Log a manual trade from broker platform"""
    try:
        action_enum = TradeAction[trade.action.upper()]
    except KeyError:
        raise HTTPException(status_code=400, detail=f"Invalid action: {trade.action}")

    trade_dict = trade.dict()
    trade_dict['action'] = action_enum

    db_trade = ManualTrade(**trade_dict)
    db.add(db_trade)

    # Update plan if linked
    if trade.plan_id:
        plan = db.query(TradingPlan).filter(TradingPlan.id == trade.plan_id).first()
        if plan:
            plan.actual_trades += 1
            if trade.pnl is not None:
                plan.actual_pnl += trade.pnl

    db.commit()
    db.refresh(db_trade)
    return db_trade


@router.get("/trades")
async def get_manual_trades(
    limit: int = 50,
    offset: int = 0,
    plan_id: Optional[int] = None,
    db: Session = Depends(get_db)
):
    """Get manual trades, optionally filtered by plan"""
    query = db.query(ManualTrade)

    if plan_id:
        query = query.filter(ManualTrade.plan_id == plan_id)

    trades = query.order_by(desc(ManualTrade.created_at)).limit(limit).offset(offset).all()
    total = query.count()

    return {"trades": trades, "total": total}


@router.get("/trades/{trade_id}")
async def get_manual_trade(
    trade_id: int,
    db: Session = Depends(get_db)
):
    """Get a specific manual trade"""
    trade = db.query(ManualTrade).filter(ManualTrade.id == trade_id).first()

    if not trade:
        raise HTTPException(status_code=404, detail="Trade not found")

    return trade


@router.put("/trades/{trade_id}")
async def update_manual_trade(
    trade_id: int,
    trade_update: ManualTradeUpdate,
    db: Session = Depends(get_db)
):
    """Update a manual trade (e.g., closing a position)"""
    db_trade = db.query(ManualTrade).filter(ManualTrade.id == trade_id).first()

    if not db_trade:
        raise HTTPException(status_code=404, detail="Trade not found")

    update_data = trade_update.dict(exclude_unset=True)

    # Calculate PnL if exit price provided
    if 'exit_price' in update_data and db_trade.exit_price is None:
        exit_price = update_data['exit_price']
        if db_trade.action == TradeAction.BUY:
            pnl = (exit_price - db_trade.entry_price) * db_trade.quantity
        else:  # SELL
            pnl = (db_trade.entry_price - exit_price) * db_trade.quantity

        update_data['pnl'] = round(pnl, 2)
        update_data['pnl_percent'] = round((pnl / (db_trade.entry_price * db_trade.quantity)) * 100, 2)

        # Update plan PnL
        if db_trade.plan_id:
            plan = db.query(TradingPlan).filter(TradingPlan.id == db_trade.plan_id).first()
            if plan:
                plan.actual_pnl += pnl

    for key, value in update_data.items():
        setattr(db_trade, key, value)

    db.commit()
    db.refresh(db_trade)
    return db_trade


@router.post("/trades/{trade_id}/screenshot")
async def upload_trade_screenshot(
    trade_id: int,
    file: UploadFile = File(...),
    db: Session = Depends(get_db)
):
    """Upload a screenshot for a trade"""
    db_trade = db.query(ManualTrade).filter(ManualTrade.id == trade_id).first()

    if not db_trade:
        raise HTTPException(status_code=404, detail="Trade not found")

    # Create uploads directory if it doesn't exist
    upload_dir = "uploads/trade_screenshots"
    os.makedirs(upload_dir, exist_ok=True)

    # Generate unique filename
    file_extension = os.path.splitext(file.filename)[1]
    unique_filename = f"{trade_id}_{uuid.uuid4()}{file_extension}"
    file_path = os.path.join(upload_dir, unique_filename)

    # Save file
    with open(file_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)

    # Update trade record
    db_trade.screenshot_url = file_path
    db.commit()

    return {"filename": unique_filename, "path": file_path}


# Journal Entries Endpoints

@router.post("/entries", status_code=201)
async def create_journal_entry(
    entry: JournalEntryCreate,
    db: Session = Depends(get_db)
):
    """Create a daily journal entry"""
    # Check if entry for this date already exists
    existing = db.query(JournalEntry).filter(
        JournalEntry.entry_date == entry.entry_date
    ).first()

    if existing:
        # Update existing entry
        update_data = entry.dict(exclude_unset=True)
        for key, value in update_data.items():
            setattr(existing, key, value)
        db.commit()
        db.refresh(existing)
        return existing

    db_entry = JournalEntry(**entry.dict())
    db.add(db_entry)
    db.commit()
    db.refresh(db_entry)
    return db_entry


@router.get("/entries/today")
async def get_today_journal(db: Session = Depends(get_db)):
    """Get today's journal entry"""
    today = date.today()
    entry = db.query(JournalEntry).filter(
        JournalEntry.entry_date == today
    ).first()

    if not entry:
        raise HTTPException(status_code=404, detail="No journal entry for today")

    return entry


@router.get("/entries")
async def get_journal_entries(
    limit: int = 30,
    offset: int = 0,
    db: Session = Depends(get_db)
):
    """Get recent journal entries"""
    entries = db.query(JournalEntry).order_by(
        desc(JournalEntry.entry_date)
    ).limit(limit).offset(offset).all()

    return {"entries": entries, "total": db.query(JournalEntry).count()}


# Decision Log Endpoints

@router.post("/decisions", status_code=201)
async def create_decision_log(
    decision: DecisionLogCreate,
    db: Session = Depends(get_db)
):
    """Log a trading decision"""
    db_decision = DecisionLog(**decision.dict())
    db.add(db_decision)
    db.commit()
    db.refresh(db_decision)
    return db_decision


@router.get("/decisions")
async def get_decisions(
    limit: int = 50,
    offset: int = 0,
    db: Session = Depends(get_db)
):
    """Get recent decisions"""
    decisions = db.query(DecisionLog).order_by(
        desc(DecisionLog.decision_time)
    ).limit(limit).offset(offset).all()

    return {"decisions": decisions, "total": db.query(DecisionLog).count()}


@router.get("/decisions/accuracy")
async def get_ai_accuracy(
    days: int = 30,
    db: Session = Depends(get_db)
):
    """Calculate AI recommendation accuracy"""
    cutoff_date = datetime.now() - timedelta(days=days)

    decisions = db.query(DecisionLog).filter(
        and_(
            DecisionLog.decision_time >= cutoff_date,
            DecisionLog.trader_action == "FOLLOWED",
            DecisionLog.outcome.isnot(None)
        )
    ).all()

    if not decisions:
        return {
            "total_decisions": 0,
            "accuracy": 0.0,
            "win_rate": 0.0,
            "avg_pnl": 0.0
        }

    wins = sum(1 for d in decisions if d.outcome == "WIN")
    total_pnl = sum(d.outcome_pnl for d in decisions if d.outcome_pnl is not None)

    return {
        "total_decisions": len(decisions),
        "wins": wins,
        "losses": len(decisions) - wins,
        "win_rate": round((wins / len(decisions)) * 100, 2),
        "avg_pnl": round(total_pnl / len(decisions), 2) if decisions else 0,
        "total_pnl": round(total_pnl, 2)
    }


# Weekly Plans Endpoints

@router.post("/weekly-plans", status_code=201)
async def create_weekly_plan(
    plan: WeeklyPlanCreate,
    db: Session = Depends(get_db)
):
    """Create a weekly trading plan"""
    db_plan = WeeklyPlan(**plan.dict())
    db.add(db_plan)
    db.commit()
    db.refresh(db_plan)
    return db_plan


@router.get("/weekly-plans/current")
async def get_current_week_plan(db: Session = Depends(get_db)):
    """Get this week's plan"""
    today = date.today()
    # Get Monday of current week
    monday = today - timedelta(days=today.weekday())

    plan = db.query(WeeklyPlan).filter(
        WeeklyPlan.week_start_date == monday
    ).first()

    if not plan:
        raise HTTPException(status_code=404, detail="No plan found for current week")

    return plan


@router.get("/weekly-plans")
async def get_weekly_plans(
    limit: int = 12,
    db: Session = Depends(get_db)
):
    """Get recent weekly plans"""
    plans = db.query(WeeklyPlan).order_by(
        desc(WeeklyPlan.week_start_date)
    ).limit(limit).all()

    return {"plans": plans, "total": db.query(WeeklyPlan).count()}
