from typing import List, Optional

from db.db import get_db
from db.models import (
    CompanyTable,
    InvestorTable,
    ProjectTable,
    SectorTable,
)
from fastapi import APIRouter, Depends, HTTPException, Query
from schemas.project_schemas import (
    InvestmentStage,
    ProjectCreate,
    ProjectData,
    ProjectUpdate,
)
from schemas.router_schemas import PaginatedResponse
from sqlalchemy.orm import Session, selectinload

router = APIRouter(tags=["Project Routes"])


@router.get("/projects", response_model=PaginatedResponse[ProjectData])
def read_projects(
    page: int = Query(1, ge=1, description="Page number (starts at 1)"),
    page_size: int = Query(10, ge=1, le=100, description="Items per page (max 100)"),
    db: Session = Depends(get_db),
):
    """Get all projects with their related data (paginated)"""
    # Calculate offset
    offset = (page - 1) * page_size

    # Get total count
    total_count = db.query(ProjectTable).count()

    # Get paginated results
    projects = (
        db.query(ProjectTable)
        .options(
            selectinload(ProjectTable.sector),
            selectinload(ProjectTable.investors),
            selectinload(ProjectTable.companies),
        )
        .offset(offset)
        .limit(page_size)
        .all()
    )

    # Transform ProjectTable objects to ProjectData format
    project_data_list = []
    for project in projects:
        project_data = ProjectData(
            project=project,
            sector=project.sector,
            investors=project.investors,
            companies=project.companies,
        )
        project_data_list.append(project_data)

    # Calculate total pages
    total_pages = (total_count + page_size - 1) // page_size

    return PaginatedResponse(
        items=project_data_list,
        total=total_count,
        page=page,
        page_size=page_size,
        total_pages=total_pages,
    )


@router.get("/projects/{project_id}", response_model=ProjectData)
def read_project(project_id: int, db: Session = Depends(get_db)):
    """Get a specific project by ID"""
    project = (
        db.query(ProjectTable)
        .options(
            selectinload(ProjectTable.sector),
            selectinload(ProjectTable.investors),
            selectinload(ProjectTable.companies),
        )
        .filter(ProjectTable.id == project_id)
        .first()
    )

    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    return ProjectData(
        project=project,
        sector=project.sector,
        investors=project.investors,
        companies=project.companies,
    )


@router.post("/projects", response_model=ProjectData)
def create_project(project: ProjectCreate, db: Session = Depends(get_db)):
    """Create a new project"""
    db_project = ProjectTable(**project.dict())
    db.add(db_project)
    db.commit()
    db.refresh(db_project)

    # Reload with relationships
    db_project = (
        db.query(ProjectTable)
        .options(
            selectinload(ProjectTable.sector),
            selectinload(ProjectTable.investors),
            selectinload(ProjectTable.companies),
        )
        .filter(ProjectTable.id == db_project.id)
        .first()
    )

    return ProjectData(
        project=db_project,
        sector=db_project.sector,
        investors=db_project.investors,
        companies=db_project.companies,
    )


@router.put("/projects/{project_id}", response_model=ProjectData)
def update_project(
    project_id: int, project: ProjectUpdate, db: Session = Depends(get_db)
):
    """Update an existing project"""
    db_project = db.query(ProjectTable).filter(ProjectTable.id == project_id).first()

    if not db_project:
        raise HTTPException(status_code=404, detail="Project not found")

    # Update only provided fields
    update_data = project.dict(exclude_unset=True)
    for key, value in update_data.items():
        setattr(db_project, key, value)

    db.commit()
    db.refresh(db_project)

    # Reload with relationships
    db_project = (
        db.query(ProjectTable)
        .options(
            selectinload(ProjectTable.sector),
            selectinload(ProjectTable.investors),
            selectinload(ProjectTable.companies),
        )
        .filter(ProjectTable.id == project_id)
        .first()
    )

    return ProjectData(
        project=db_project,
        sector=db_project.sector,
        investors=db_project.investors,
        companies=db_project.companies,
    )


@router.delete("/projects/{project_id}")
def delete_project(project_id: int, db: Session = Depends(get_db)):
    """Delete a project"""
    db_project = db.query(ProjectTable).filter(ProjectTable.id == project_id).first()

    if not db_project:
        raise HTTPException(status_code=404, detail="Project not found")

    db.delete(db_project)
    db.commit()

    return {"message": "Project deleted successfully"}


@router.get("/projects/filter", response_model=PaginatedResponse[ProjectData])
def filter_projects(
    stage: Optional[InvestmentStage] = Query(
        None, description="Filter by project stage"
    ),
    min_valuation: Optional[int] = Query(None, description="Minimum valuation"),
    max_valuation: Optional[int] = Query(None, description="Maximum valuation"),
    location: Optional[str] = Query(None, description="Location (partial match)"),
    sector: Optional[str] = Query(None, description="Sector name (partial match)"),
    investor_name: Optional[str] = Query(
        None, description="Investor name (partial match)"
    ),
    company_name: Optional[str] = Query(
        None, description="Company name (partial match)"
    ),
    page: int = Query(1, ge=1, description="Page number (starts at 1)"),
    page_size: int = Query(10, ge=1, le=100, description="Items per page (max 100)"),
    db: Session = Depends(get_db),
):
    """Filter projects based on various criteria (paginated)"""

    # Start with base query
    query = db.query(ProjectTable).options(
        selectinload(ProjectTable.sector),
        selectinload(ProjectTable.investors),
        selectinload(ProjectTable.companies),
    )

    # Apply filters
    if stage:
        query = query.filter(ProjectTable.stage == stage)

    if min_valuation is not None:
        query = query.filter(ProjectTable.valuation >= min_valuation)

    if max_valuation is not None:
        query = query.filter(ProjectTable.valuation <= max_valuation)

    if location:
        query = query.filter(ProjectTable.location.ilike(f"%{location}%"))

    if sector:
        query = query.join(ProjectTable.sector).filter(
            SectorTable.name.ilike(f"%{sector}%")
        )

    if investor_name:
        query = query.join(ProjectTable.investors).filter(
            InvestorTable.name.ilike(f"%{investor_name}%")
        )

    if company_name:
        query = query.join(ProjectTable.companies).filter(
            CompanyTable.name.ilike(f"%{company_name}%")
        )

    # Get total count before pagination
    total_count = query.count()

    # Calculate offset and apply pagination
    offset = (page - 1) * page_size
    projects = query.offset(offset).limit(page_size).all()

    # Transform to ProjectData format
    project_data_list = []
    for project in projects:
        project_data = ProjectData(
            project=project,
            sector=project.sector,
            investors=project.investors,
            companies=project.companies,
        )
        project_data_list.append(project_data)

    # Calculate total pages
    total_pages = (total_count + page_size - 1) // page_size

    return PaginatedResponse(
        items=project_data_list,
        total=total_count,
        page=page,
        page_size=page_size,
        total_pages=total_pages,
    )


# Association management routes
@router.post("/projects/{project_id}/investors/{investor_id}")
def add_investor_to_project(
    project_id: int, investor_id: int, db: Session = Depends(get_db)
):
    """Add an investor to a project"""
    # Check if project exists
    project = db.query(ProjectTable).filter(ProjectTable.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # Check if investor exists
    investor = db.query(InvestorTable).filter(InvestorTable.id == investor_id).first()
    if not investor:
        raise HTTPException(status_code=404, detail="Investor not found")

    # Check if association already exists
    if investor in project.investors:
        raise HTTPException(
            status_code=400, detail="Investor already associated with project"
        )

    # Add association
    project.investors.append(investor)
    db.commit()

    return {"message": "Investor added to project successfully"}


@router.delete("/projects/{project_id}/investors/{investor_id}")
def remove_investor_from_project(
    project_id: int, investor_id: int, db: Session = Depends(get_db)
):
    """Remove an investor from a project"""
    # Check if project exists
    project = db.query(ProjectTable).filter(ProjectTable.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # Check if investor exists
    investor = db.query(InvestorTable).filter(InvestorTable.id == investor_id).first()
    if not investor:
        raise HTTPException(status_code=404, detail="Investor not found")

    # Check if association exists
    if investor not in project.investors:
        raise HTTPException(
            status_code=400, detail="Investor not associated with project"
        )

    # Remove association
    project.investors.remove(investor)
    db.commit()

    return {"message": "Investor removed from project successfully"}


@router.post("/projects/{project_id}/companies/{company_id}")
def add_company_to_project(
    project_id: int, company_id: int, db: Session = Depends(get_db)
):
    """Add a company to a project"""
    # Check if project exists
    project = db.query(ProjectTable).filter(ProjectTable.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # Check if company exists
    company = db.query(CompanyTable).filter(CompanyTable.id == company_id).first()
    if not company:
        raise HTTPException(status_code=404, detail="Company not found")

    # Check if association already exists
    if company in project.companies:
        raise HTTPException(
            status_code=400, detail="Company already associated with project"
        )

    # Add association
    project.companies.append(company)
    db.commit()

    return {"message": "Company added to project successfully"}


@router.delete("/projects/{project_id}/companies/{company_id}")
def remove_company_from_project(
    project_id: int, company_id: int, db: Session = Depends(get_db)
):
    """Remove a company from a project"""
    # Check if project exists
    project = db.query(ProjectTable).filter(ProjectTable.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # Check if company exists
    company = db.query(CompanyTable).filter(CompanyTable.id == company_id).first()
    if not company:
        raise HTTPException(status_code=404, detail="Company not found")

    # Check if association exists
    if company not in project.companies:
        raise HTTPException(
            status_code=400, detail="Company not associated with project"
        )

    # Remove association
    project.companies.remove(company)
    db.commit()

    return {"message": "Company removed from project successfully"}


@router.post("/projects/{project_id}/sectors/{sector_id}")
def add_sector_to_project(
    project_id: int, sector_id: int, db: Session = Depends(get_db)
):
    """Add a sector to a project"""
    # Check if project exists
    project = db.query(ProjectTable).filter(ProjectTable.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # Check if sector exists
    sector = db.query(SectorTable).filter(SectorTable.id == sector_id).first()
    if not sector:
        raise HTTPException(status_code=404, detail="Sector not found")

    # Check if association already exists
    if sector in project.sector:
        raise HTTPException(
            status_code=400, detail="Sector already associated with project"
        )

    # Add association
    project.sector.append(sector)
    db.commit()

    return {"message": "Sector added to project successfully"}


@router.delete("/projects/{project_id}/sectors/{sector_id}")
def remove_sector_from_project(
    project_id: int, sector_id: int, db: Session = Depends(get_db)
):
    """Remove a sector from a project"""
    # Check if project exists
    project = db.query(ProjectTable).filter(ProjectTable.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # Check if sector exists
    sector = db.query(SectorTable).filter(SectorTable.id == sector_id).first()
    if not sector:
        raise HTTPException(status_code=404, detail="Sector not found")

    # Check if association exists
    if sector not in project.sector:
        raise HTTPException(
            status_code=400, detail="Sector not associated with project"
        )

    # Remove association
    project.sector.remove(sector)
    db.commit()

    return {"message": "Sector removed from project successfully"}


# Bulk association management
@router.post("/projects/{project_id}/investors")
def add_multiple_investors_to_project(
    project_id: int, investor_ids: List[int], db: Session = Depends(get_db)
):
    """Add multiple investors to a project"""
    # Check if project exists
    project = db.query(ProjectTable).filter(ProjectTable.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # Get all investors
    investors = db.query(InvestorTable).filter(InvestorTable.id.in_(investor_ids)).all()

    if len(investors) != len(investor_ids):
        raise HTTPException(status_code=404, detail="One or more investors not found")

    # Add associations (only if not already associated)
    added_count = 0
    for investor in investors:
        if investor not in project.investors:
            project.investors.append(investor)
            added_count += 1

    db.commit()

    return {"message": f"Added {added_count} investors to project successfully"}


@router.post("/projects/{project_id}/companies")
def add_multiple_companies_to_project(
    project_id: int, company_ids: List[int], db: Session = Depends(get_db)
):
    """Add multiple companies to a project"""
    # Check if project exists
    project = db.query(ProjectTable).filter(ProjectTable.id == project_id).first()
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # Get all companies
    companies = db.query(CompanyTable).filter(CompanyTable.id.in_(company_ids)).all()

    if len(companies) != len(company_ids):
        raise HTTPException(status_code=404, detail="One or more companies not found")

    # Add associations (only if not already associated)
    added_count = 0
    for company in companies:
        if company not in project.companies:
            project.companies.append(company)
            added_count += 1

    db.commit()

    return {"message": f"Added {added_count} companies to project successfully"}
