from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import List, Optional
from datetime import datetime
from . import repository
from .schema import (
    WatchlistCreate,
    WatchlistUpdate,
    WatchlistResponse,
    WatchlistitemCreate,
    WatchlistitemUpdate,
    WatchlistitemResponse,
    WatchlistDetailResponse,
)
from user_management import repository as user_repo
from stock_management import repository as stock_repo


def _get_or_raise(db: Session, entity_id: str, repo_module) -> any:
    entity = repo_module.get_by_id(db, entity_id)
    if not entity:
        raise HTTPException(status_code=404, detail="Resource not found")
    return entity


def create_watchlist(db: Session, data: WatchlistCreate) -> WatchlistResponse:
    user = user_repo.get_by_id(db, data.user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    
    existing = repository.get_by_user_and_name(db, data.user_id, data.name)
    if existing:
        raise HTTPException(
            status_code=409,
            detail="Watchlist name must be unique per user"
        )
    
    user_watchlist_count = repository.count_by_user(db, data.user_id)
    if user_watchlist_count >= 10:
        raise HTTPException(
            status_code=400,
            detail="Users can create maximum 10 watchlists"
        )
    
    try:
        watchlist = repository.create(db, data.model_dump())
        db.commit()
        db.refresh(watchlist)
        return WatchlistResponse.model_validate(watchlist)
    except Exception:
        db.rollback()
        raise


def list_watchlists(
    db: Session,
    limit: int,
    offset: int,
    user_id: Optional[str] = None,
    is_default: Optional[bool] = None,
) -> List[WatchlistResponse]:
    filters = {}
    if user_id:
        filters["user_id"] = user_id
    if is_default is not None:
        filters["is_default"] = is_default
    
    watchlists = repository.list_all(db, limit, offset, **filters)
    return [WatchlistResponse.model_validate(w) for w in watchlists]


def get_watchlist(db: Session, watchlist_id: str) -> WatchlistResponse:
    watchlist = _get_or_raise(db, watchlist_id, repository)
    return WatchlistResponse.model_validate(watchlist)


def update_watchlist(
    db: Session, watchlist_id: str, data: WatchlistUpdate
) -> WatchlistResponse:
    watchlist = _get_or_raise(db, watchlist_id, repository)
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "name" in update_data:
        existing = repository.get_by_user_and_name(
            db, watchlist.user_id, update_data["name"]
        )
        if existing and existing.id != watchlist_id:
            raise HTTPException(
                status_code=409,
                detail="Watchlist name must be unique per user"
            )
    
    try:
        updated = repository.update(db, watchlist_id, update_data)
        db.commit()
        db.refresh(updated)
        return WatchlistResponse.model_validate(updated)
    except Exception:
        db.rollback()
        raise


def delete_watchlist(db: Session, watchlist_id: str) -> dict:
    watchlist = _get_or_raise(db, watchlist_id, repository)
    
    if watchlist.is_default:
        raise HTTPException(
            status_code=400,
            detail="Default watchlist cannot be deleted"
        )
    
    try:
        repository.delete(db, watchlist_id)
        db.commit()
        return {"message": "Watchlist deleted successfully"}
    except Exception:
        db.rollback()
        raise


def get_watchlist_with_details(db: Session, watchlist_id: str) -> WatchlistDetailResponse:
    watchlist = repository.get_with_details(db, watchlist_id)
    if not watchlist:
        raise HTTPException(status_code=404, detail="Watchlist not found")
    
    response_data = {
        "id": watchlist.id,
        "user_id": watchlist.user_id,
        "name": watchlist.name,
        "description": watchlist.description,
        "is_default": watchlist.is_default,
        "sort_order": watchlist.sort_order,
        "created_at": watchlist.created_at,
        "updated_at": watchlist.updated_at,
        "watchlist_items": []
    }
    
    for item in watchlist.watchlist_items:
        stock_data = {
            "id": item.stock.id,
            "ticker_symbol": item.stock.ticker_symbol,
            "company_name": item.stock.company_name,
            "current_price": item.stock.current_price,
            "sector_name": item.stock.sector.name if item.stock.sector else None,
            "exchange_name": item.stock.exchange.name if item.stock.exchange else None,
        }
        
        item_data = {
            "id": item.id,
            "watchlist_id": item.watchlist_id,
            "stock_id": item.stock_id,
            "added_date": item.added_date,
            "notes": item.notes,
            "sort_order": item.sort_order,
            "stock": stock_data
        }
        response_data["watchlist_items"].append(item_data)
    
    return WatchlistDetailResponse.model_validate(response_data)


def add_stock_to_watchlist(
    db: Session, watchlist_id: str, stock_id: str, notes: Optional[str] = None
) -> WatchlistitemResponse:
    watchlist = _get_or_raise(db, watchlist_id, repository)
    
    stock = stock_repo.get_by_id(db, stock_id)
    if not stock:
        raise HTTPException(status_code=404, detail="Stock not found")
    
    existing_item = repository.get_watchlistitem_by_watchlist_and_stock(
        db, watchlist_id, stock_id
    )
    if existing_item:
        raise HTTPException(
            status_code=409,
            detail="Stock already exists in this watchlist"
        )
    
    item_count = repository.count_items_in_watchlist(db, watchlist_id)
    if item_count >= 100:
        raise HTTPException(
            status_code=400,
            detail="Watchlists can contain maximum 100 stocks"
        )
    
    item_data = {
        "watchlist_id": watchlist_id,
        "stock_id": stock_id,
        "added_date": datetime.utcnow(),
        "notes": notes,
        "sort_order": item_count + 1
    }
    
    try:
        item = repository.create_watchlistitem(db, item_data)
        db.commit()
        db.refresh(item)
        return WatchlistitemResponse.model_validate(item)
    except Exception:
        db.rollback()
        raise


def remove_stock_from_watchlist(
    db: Session, watchlist_id: str, stock_id: str
) -> dict:
    watchlist = _get_or_raise(db, watchlist_id, repository)
    
    item = repository.get_watchlistitem_by_watchlist_and_stock(
        db, watchlist_id, stock_id
    )
    if not item:
        raise HTTPException(
            status_code=404,
            detail="Stock not found in this watchlist"
        )
    
    try:
        repository.delete_watchlistitem(db, item.id)
        db.commit()
        return {"message": "Stock removed from watchlist successfully"}
    except Exception:
        db.rollback()
        raise


def create_watchlistitem(db: Session, data: WatchlistitemCreate) -> WatchlistitemResponse:
    watchlist = _get_or_raise(db, data.watchlist_id, repository)
    
    stock = stock_repo.get_by_id(db, data.stock_id)
    if not stock:
        raise HTTPException(status_code=404, detail="Stock not found")
    
    existing_item = repository.get_watchlistitem_by_watchlist_and_stock(
        db, data.watchlist_id, data.stock_id
    )
    if existing_item:
        raise HTTPException(
            status_code=409,
            detail="Stock already exists in this watchlist"
        )
    
    item_count = repository.count_items_in_watchlist(db, data.watchlist_id)
    if item_count >= 100:
        raise HTTPException(
            status_code=400,
            detail="Watchlists can contain maximum 100 stocks"
        )
    
    try:
        item = repository.create_watchlistitem(db, data.model_dump())
        db.commit()
        db.refresh(item)
        return WatchlistitemResponse.model_validate(item)
    except Exception:
        db.rollback()
        raise


def list_watchlistitems(
    db: Session,
    limit: int,
    offset: int,
    watchlist_id: Optional[str] = None,
    stock_id: Optional[str] = None,
) -> List[WatchlistitemResponse]:
    filters = {}
    if watchlist_id:
        filters["watchlist_id"] = watchlist_id
    if stock_id:
        filters["stock_id"] = stock_id
    
    items = repository.list_watchlistitems(db, limit, offset, **filters)
    return [WatchlistitemResponse.model_validate(item) for item in items]


def get_watchlistitem(db: Session, item_id: str) -> WatchlistitemResponse:
    item = repository.get_watchlistitem_by_id(db, item_id)
    if not item:
        raise HTTPException(status_code=404, detail="Watchlist item not found")
    return WatchlistitemResponse.model_validate(item)


def update_watchlistitem(
    db: Session, item_id: str, data: WatchlistitemUpdate
) -> WatchlistitemResponse:
    item = repository.get_watchlistitem_by_id(db, item_id)
    if not item:
        raise HTTPException(status_code=404, detail="Watchlist item not found")
    
    update_data = data.model_dump(exclude_unset=True)
    
    try:
        updated = repository.update_watchlistitem(db, item_id, update_data)
        db.commit()
        db.refresh(updated)
        return WatchlistitemResponse.model_validate(updated)
    except Exception:
        db.rollback()
        raise


def delete_watchlistitem(db: Session, item_id: str) -> dict:
    item = repository.get_watchlistitem_by_id(db, item_id)
    if not item:
        raise HTTPException(status_code=404, detail="Watchlist item not found")
    
    try:
        repository.delete_watchlistitem(db, item_id)
        db.commit()
        return {"message": "Watchlist item deleted successfully"}
    except Exception:
        db.rollback()
        raise