"""
Calendar Service - FastAPI application for CalDAV integration
Provides unified API for Sofiia agent to manage calendars
"""

import os
import logging
from contextlib import asynccontextmanager
from typing import Optional, List, Dict, Any
from datetime import datetime

from fastapi import FastAPI, HTTPException, Header, Depends, Request
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base

from calendar_client import CalDAVClient
from storage import CalendarStorage
from reminder_worker import ReminderWorker

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Database
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./calendar.db")
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()


def get_db():
    """Database session dependency"""
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


# =============================================================================
# MODELS
# =============================================================================

class ConnectRequest(BaseModel):
    workspace_id: str
    user_id: str
    username: str
    password: str
    provider: str = "radicale"


class CreateEventRequest(BaseModel):
    title: str
    start: str  # ISO 8601
    end: str    # ISO 8601
    timezone: str = "Europe/Kiev"
    location: Optional[str] = None
    description: Optional[str] = None
    attendees: Optional[List[str]] = None
    idempotency_key: Optional[str] = None


class UpdateEventRequest(BaseModel):
    title: Optional[str] = None
    start: Optional[str] = None
    end: Optional[str] = None
    timezone: str = "Europe/Kiev"
    location: Optional[str] = None
    description: Optional[str] = None


class SetReminderRequest(BaseModel):
    event_uid: str
    remind_at: str  # ISO 8601
    channel: str = "inapp"  # inapp, telegram, email


class CalendarToolRequest(BaseModel):
    action: str
    workspace_id: str
    user_id: str
    provider: str = "radicale"
    account_id: Optional[str] = None
    calendar_id: Optional[str] = None
    params: Dict[str, Any] = Field(default_factory=dict)


# =============================================================================
# APP SETUP
# =============================================================================

@asynccontextmanager
async def lifespan(app: FastAPI):
    """Startup and shutdown events"""
    # Startup
    logger.info("Starting calendar service...")
    
    # Create tables
    Base.metadata.create_all(bind=engine)
    
    # Initialize storage
    storage = CalendarStorage(SessionLocal())
    app.state.storage = storage
    
    # Initialize reminder worker
    worker = ReminderWorker(storage)
    app.state.reminder_worker = worker
    worker.start()
    
    yield
    
    # Shutdown
    logger.info("Shutting down calendar service...")
    worker.stop()


app = FastAPI(
    title="Calendar Service",
    description="CalDAV integration for DAARION agents",
    version="1.0.0",
    lifespan=lifespan
)

# CORS
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# =============================================================================
# AUTH & RBAC
# =============================================================================

async def verify_agent(
    x_agent_id: str = Header(default="anonymous"),
    x_workspace_id: str = Header(default=None)
) -> Dict[str, str]:
    """Verify agent authorization"""
    # In production, verify against RBAC system
    return {
        "agent_id": x_agent_id,
        "workspace_id": x_workspace_id or "default"
    }


# =============================================================================
# CONNECTION MANAGEMENT
# =============================================================================

@app.post("/v1/calendar/connect/radicale")
async def connect_radicale(
    request: ConnectRequest,
    auth: Dict = Depends(verify_agent),
    db=Depends(get_db)
):
    """Connect a Radicale CalDAV account"""
    storage: CalendarStorage = app.state.storage
    
    try:
        # Create CalDAV client and test connection
        client = CalDAVClient(
            server_url=os.getenv("RADICALE_URL", "https://caldav.daarion.space"),
            username=request.username,
            password=request.password
        )
        
        # Test connection
        calendars = client.list_calendars()
        
        # Find default calendar
        default_cal = None
        for cal in calendars:
            if cal.get("display_name") in ["default", "Calendar", "Personal"]:
                default_cal = cal.get("id")
                break
        
        if not default_cal and calendars:
            default_cal = calendars[0].get("id")
        
        # Store account
        account = storage.create_account(
            workspace_id=request.workspace_id,
            user_id=request.user_id,
            provider=request.provider,
            username=request.username,
            password=request.password,
            principal_url=client.principal_url,
            default_calendar_id=default_cal
        )
        
        return {
            "status": "connected",
            "account_id": account.id,
            "calendars": calendars,
            "default_calendar": default_cal
        }
        
    except Exception as e:
        logger.error(f"Failed to connect account: {e}")
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/v1/calendar/accounts")
async def list_accounts(
    workspace_id: str,
    user_id: str,
    auth: Dict = Depends(verify_agent),
    db=Depends(get_db)
):
    """List connected calendar accounts"""
    storage: CalendarStorage = app.state.storage
    
    accounts = storage.list_accounts(workspace_id, user_id)
    
    return {
        "accounts": [
            {
                "id": a.id,
                "provider": a.provider,
                "username": a.username,
                "default_calendar_id": a.default_calendar_id,
                "created_at": a.created_at.isoformat()
            }
            for a in accounts
        ]
    }


# =============================================================================
# CALENDAR OPERATIONS
# =============================================================================

@app.get("/v1/calendar/calendars")
async def list_calendars(
    account_id: str,
    auth: Dict = Depends(verify_agent),
    db=Depends(get_db)
):
    """List calendars for an account"""
    storage: CalendarStorage = app.state.storage
    
    account = storage.get_account(account_id)
    if not account:
        raise HTTPException(status_code=404, detail="Account not found")
    
    client = CalDAVClient(
        server_url=os.getenv("RADICALE_URL", "https://caldav.daarion.space"),
        username=account.username,
        password=account.password
    )
    
    calendars = client.list_calendars()
    
    return {"calendars": calendars}


@app.get("/v1/calendar/events")
async def list_events(
    account_id: str,
    calendar_id: Optional[str] = None,
    time_min: Optional[str] = None,
    time_max: Optional[str] = None,
    q: Optional[str] = None,
    auth: Dict = Depends(verify_agent),
    db=Depends(get_db)
):
    """List events from calendar"""
    storage: CalendarStorage = app.state.storage
    
    account = storage.get_account(account_id)
    if not account:
        raise HTTPException(status_code=404, detail="Account not found")
    
    calendar_id = calendar_id or account.default_calendar_id
    
    client = CalDAVClient(
        server_url=os.getenv("RADICALE_URL", "https://caldav.daarion.space"),
        username=account.username,
        password=account.password
    )
    
    events = client.list_events(
        calendar_id=calendar_id,
        time_min=time_min,
        time_max=time_max
    )
    
    # Filter by query if provided
    if q:
        events = [e for e in events if q.lower() in e.get("title", "").lower()]
    
    return {"events": events}


@app.get("/v1/calendar/events/{uid}")
async def get_event(
    uid: str,
    account_id: str,
    calendar_id: Optional[str] = None,
    auth: Dict = Depends(verify_agent),
    db=Depends(get_db)
):
    """Get single event"""
    storage: CalendarStorage = app.state.storage
    
    account = storage.get_account(account_id)
    if not account:
        raise HTTPException(status_code=404, detail="Account not found")
    
    calendar_id = calendar_id or account.default_calendar_id
    
    client = CalDAVClient(
        server_url=os.getenv("RADICALE_URL", "https://caldav.daarion.space"),
        username=account.username,
        password=account.password
    )
    
    event = client.get_event(uid, calendar_id)
    
    if not event:
        raise HTTPException(status_code=404, detail="Event not found")
    
    return {"event": event}


@app.post("/v1/calendar/events")
async def create_event(
    request: CreateEventRequest,
    account_id: str,
    calendar_id: Optional[str] = None,
    auth: Dict = Depends(verify_agent),
    db=Depends(get_db)
):
    """Create new calendar event"""
    storage: CalendarStorage = app.state.storage
    
    # Check idempotency
    if request.idempotency_key:
        existing = storage.get_by_idempotency_key(request.idempotency_key)
        if existing:
            return {
                "status": "already_exists",
                "event_uid": existing.event_uid,
                "message": "Event already exists"
            }
    
    account = storage.get_account(account_id)
    if not account:
        raise HTTPException(status_code=404, detail="Account not found")
    
    calendar_id = calendar_id or account.default_calendar_id
    
    client = CalDAVClient(
        server_url=os.getenv("RADICALE_URL", "https://caldav.daarion.space"),
        username=account.username,
        password=account.password
    )
    
    event_uid = client.create_event(
        calendar_id=calendar_id,
        title=request.title,
        start=request.start,
        end=request.end,
        timezone=request.timezone,
        location=request.location,
        description=request.description,
        attendees=request.attendees
    )
    
    # Store idempotency key
    if request.idempotency_key:
        storage.store_idempotency_key(
            request.idempotency_key,
            account.workspace_id,
            account.user_id,
            event_uid
        )
    
    return {
        "status": "created",
        "event_uid": event_uid
    }


@app.patch("/v1/calendar/events/{uid}")
async def update_event(
    uid: str,
    request: UpdateEventRequest,
    account_id: str,
    calendar_id: Optional[str] = None,
    auth: Dict = Depends(verify_agent),
    db=Depends(get_db)
):
    """Update existing event"""
    storage: CalendarStorage = app.state.storage
    
    account = storage.get_account(account_id)
    if not account:
        raise HTTPException(status_code=404, detail="Account not found")
    
    calendar_id = calendar_id or account.default_calendar_id
    
    client = CalDAVClient(
        server_url=os.getenv("RADICALE_URL", "https://caldav.daarion.space"),
        username=account.username,
        password=account.password
    )
    
    client.update_event(
        uid=uid,
        calendar_id=calendar_id,
        title=request.title,
        start=request.start,
        end=request.end,
        timezone=request.timezone,
        location=request.location,
        description=request.description
    )
    
    return {"status": "updated", "event_uid": uid}


@app.delete("/v1/calendar/events/{uid}")
async def delete_event(
    uid: str,
    account_id: str,
    calendar_id: Optional[str] = None,
    auth: Dict = Depends(verify_agent),
    db=Depends(get_db)
):
    """Delete event"""
    storage: CalendarStorage = app.state.storage
    
    account = storage.get_account(account_id)
    if not account:
        raise HTTPException(status_code=404, detail="Account not found")
    
    calendar_id = calendar_id or account.default_calendar_id
    
    client = CalDAVClient(
        server_url=os.getenv("RADICALE_URL", "https://caldav.daarion.space"),
        username=account.username,
        password=account.password
    )
    
    client.delete_event(uid, calendar_id)
    
    return {"status": "deleted", "event_uid": uid}


# =============================================================================
# REMINDERS
# =============================================================================

@app.post("/v1/calendar/reminders")
async def set_reminder(
    request: SetReminderRequest,
    account_id: str,
    auth: Dict = Depends(verify_agent),
    db=Depends(get_db)
):
    """Set event reminder"""
    storage: CalendarStorage = app.state.storage
    
    reminder = storage.create_reminder(
        workspace_id=auth["workspace_id"],
        user_id=auth["user_id"],
        account_id=account_id,
        event_uid=request.event_uid,
        remind_at=request.remind_at,
        channel=request.channel
    )
    
    return {
        "status": "created",
        "reminder_id": reminder.id
    }


# =============================================================================
# CALENDAR TOOL FOR SOFIIA
# =============================================================================

@app.post("/v1/tools/calendar")
async def calendar_tool(
    request: CalendarToolRequest,
    auth: Dict = Depends(verify_agent),
    db=Depends(get_db)
):
    """
    Unified calendar tool endpoint for Sofiia agent.
    
    Actions:
    - connect: Connect Radicale account
    - list_calendars: List available calendars
    - list_events: List events in calendar
    - get_event: Get single event
    - create_event: Create new event
    - update_event: Update event
    - delete_event: Delete event
    - set_reminder: Set reminder
    """
    storage: CalendarStorage = app.state.storage
    
    try:
        if request.action == "connect":
            # Connect account
            params = request.params
            connect_req = ConnectRequest(
                workspace_id=request.workspace_id,
                user_id=request.user_id,
                username=params.get("username"),
                password=params.get("password")
            )
            # Reuse connect logic
            result = await connect_radicale(connect_req, auth, db)
            return {"status": "succeeded", "data": result}
        
        elif request.action == "list_calendars":
            if not request.account_id:
                raise HTTPException(status_code=400, detail="account_id required")
            return await list_calendars(request.account_id, auth, db)
        
        elif request.action == "list_events":
            if not request.account_id:
                raise HTTPException(status_code=400, detail="account_id required")
            params = request.params
            return await list_events(
                request.account_id,
                request.calendar_id,
                params.get("time_min"),
                params.get("time_max"),
                params.get("q"),
                auth,
                db
            )
        
        elif request.action == "get_event":
            if not request.account_id:
                raise HTTPException(status_code=400, detail="account_id required")
            params = request.params
            return await get_event(
                params.get("uid"),
                request.account_id,
                request.calendar_id,
                auth,
                db
            )
        
        elif request.action == "create_event":
            if not request.account_id:
                raise HTTPException(status_code=400, detail="account_id required")
            params = request.params
            create_req = CreateEventRequest(**params)
            return await create_event(
                create_req,
                request.account_id,
                request.calendar_id,
                auth,
                db
            )
        
        elif request.action == "update_event":
            if not request.account_id:
                raise HTTPException(status_code=400, detail="account_id required")
            params = request.params
            update_req = UpdateEventRequest(**params)
            return await update_event(
                params.get("uid"),
                update_req,
                request.account_id,
                request.calendar_id,
                auth,
                db
            )
        
        elif request.action == "delete_event":
            if not request.account_id:
                raise HTTPException(status_code=400, detail="account_id required")
            params = request.params
            return await delete_event(
                params.get("uid"),
                request.account_id,
                request.calendar_id,
                auth,
                db
            )
        
        elif request.action == "set_reminder":
            if not request.account_id:
                raise HTTPException(status_code=400, detail="account_id required")
            params = request.params
            reminder_req = SetReminderRequest(**params)
            return await set_reminder(reminder_req, request.account_id, auth, db)
        
        else:
            raise HTTPException(status_code=400, detail=f"Unknown action: {request.action}")
    
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"Calendar tool error: {e}")
        return {
            "status": "failed",
            "error": {
                "code": "internal_error",
                "message": str(e),
                "retryable": False
            }
        }


# =============================================================================
# HEALTH & METRICS
# =============================================================================

@app.get("/health")
async def health():
    """Health check"""
    return {"status": "healthy"}


@app.get("/metrics")
async def metrics():
    """Service metrics"""
    storage: CalendarStorage = app.state.storage
    worker: ReminderWorker = app.state.reminder_worker
    
    return {
        "accounts_count": storage.count_accounts(),
        "reminders_pending": storage.count_pending_reminders(),
        "worker_status": worker.get_status()
    }
