"""
Data Ingestion Sources Router

CRUD API for persistent data ingestion source entities.
Sources represent configured connections to external platforms
(AWS, GitHub, Azure, etc.) that sync data into the Context Graph.
"""
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, Query
from typing import List, Optional, Dict, Any
from sqlalchemy.orm import Session
from pydantic import BaseModel, Field
import structlog

from control_plane_api.app.database import get_db
from control_plane_api.app.middleware.auth import get_current_organization
from control_plane_api.app.models.data_ingestion_source import (
    DataIngestionSource,
    DataIngestionSourceStatus,
    CredentialMethod,
    ScheduleType,
)

logger = structlog.get_logger()

router = APIRouter(prefix="/data-ingestion/sources", tags=["data-ingestion-sources"])


# ============================================================================
# Pydantic Models
# ============================================================================

class CreateDataIngestionSourceRequest(BaseModel):
    """Request to create a data ingestion source"""
    name: str = Field(..., min_length=1, max_length=255, description="Source name")
    description: Optional[str] = Field(None, description="Description")
    intel_type: str = Field(..., min_length=1, max_length=100, description="Intel type (aws, azure, github, etc.)")
    credential_method: CredentialMethod = Field(..., description="How credentials are provided")
    credential_ref: Optional[str] = Field(None, max_length=500, description="Integration ID or secret name")
    config: Dict[str, Any] = Field(default_factory=dict, description="Intel-type-specific configuration")
    schedule_type: ScheduleType = Field(default=ScheduleType.MANUAL, description="Schedule type")
    cron_expression: Optional[str] = Field(None, max_length=100, description="Cron expression if schedule_type=cron")
    tags: List[str] = Field(default_factory=list, description="Tags for organization")


class UpdateDataIngestionSourceRequest(BaseModel):
    """Request to update a data ingestion source (partial update)"""
    name: Optional[str] = Field(None, min_length=1, max_length=255)
    description: Optional[str] = None
    credential_method: Optional[CredentialMethod] = None
    credential_ref: Optional[str] = Field(None, max_length=500)
    config: Optional[Dict[str, Any]] = None
    schedule_type: Optional[ScheduleType] = None
    cron_expression: Optional[str] = Field(None, max_length=100)
    status: Optional[DataIngestionSourceStatus] = None
    tags: Optional[List[str]] = None


class UpdateSyncStatusRequest(BaseModel):
    """Request to update sync tracking fields after a workflow completes"""
    last_sync_at: Optional[datetime] = None
    last_sync_status: Optional[str] = Field(None, max_length=50, description="success/failed/running")
    last_sync_workflow_id: Optional[str] = Field(None, max_length=500)
    last_error: Optional[str] = None
    increment_total_runs: bool = Field(default=False, description="Increment total_runs counter")
    increment_success: bool = Field(default=False, description="Increment success_count counter")
    increment_failure: bool = Field(default=False, description="Increment failure_count counter")


class DataIngestionSourceResponse(BaseModel):
    """Data ingestion source response"""
    id: str
    organization_id: str
    name: str
    description: Optional[str]
    intel_type: str
    credential_method: str
    credential_ref: Optional[str]
    config: Dict[str, Any]
    schedule_type: str
    cron_expression: Optional[str]
    status: str
    last_sync_at: Optional[str]
    last_sync_status: Optional[str]
    last_sync_workflow_id: Optional[str]
    last_error: Optional[str]
    total_runs: int
    success_count: int
    failure_count: int
    tags: List[str]
    created_at: datetime
    updated_at: datetime
    created_by: Optional[str]

    class Config:
        from_attributes = True


# ============================================================================
# API Endpoints
# ============================================================================

@router.post("", response_model=DataIngestionSourceResponse, status_code=201)
async def create_data_ingestion_source(
    request: CreateDataIngestionSourceRequest,
    organization: dict = Depends(get_current_organization),
    db: Session = Depends(get_db),
):
    """Create a new data ingestion source for the organization."""
    org_id = organization["id"]
    user_id = organization.get("user_id")

    # Check name uniqueness within org (excluding deleted)
    existing = db.query(DataIngestionSource).filter(
        DataIngestionSource.organization_id == org_id,
        DataIngestionSource.name == request.name,
        DataIngestionSource.status != DataIngestionSourceStatus.DELETED,
    ).first()

    if existing:
        raise HTTPException(
            status_code=409,
            detail=f"Data ingestion source with name '{request.name}' already exists",
        )

    source = DataIngestionSource(
        organization_id=org_id,
        name=request.name,
        description=request.description,
        intel_type=request.intel_type,
        credential_method=request.credential_method,
        credential_ref=request.credential_ref,
        config=request.config,
        schedule_type=request.schedule_type,
        cron_expression=request.cron_expression,
        tags=request.tags,
        status=DataIngestionSourceStatus.ACTIVE,
        created_by=user_id,
    )

    db.add(source)
    db.commit()
    db.refresh(source)

    logger.info(
        "data_ingestion_source_created",
        org_id=org_id,
        source_id=str(source.id)[:8],
        source_name=source.name,
        intel_type=source.intel_type,
    )

    return source


@router.get("", response_model=List[DataIngestionSourceResponse])
async def list_data_ingestion_sources(
    organization: dict = Depends(get_current_organization),
    db: Session = Depends(get_db),
    intel_type: Optional[str] = Query(None, description="Filter by intel type"),
    status: Optional[DataIngestionSourceStatus] = Query(None, description="Filter by status"),
    tags: Optional[str] = Query(None, description="Filter by tags (comma-separated)"),
):
    """List all data ingestion sources for the organization."""
    org_id = organization["id"]

    query = db.query(DataIngestionSource).filter(
        DataIngestionSource.organization_id == org_id
    )

    if intel_type:
        query = query.filter(DataIngestionSource.intel_type == intel_type)

    if status:
        query = query.filter(DataIngestionSource.status == status)
    else:
        # Exclude deleted by default
        query = query.filter(DataIngestionSource.status != DataIngestionSourceStatus.DELETED)

    if tags:
        tag_list = [t.strip() for t in tags.split(",")]
        for tag in tag_list:
            query = query.filter(DataIngestionSource.tags.contains([tag]))

    sources = query.order_by(DataIngestionSource.created_at.desc()).all()

    logger.info(
        "data_ingestion_sources_listed",
        org_id=org_id,
        count=len(sources),
        filters={"intel_type": intel_type, "status": status, "tags": tags},
    )

    return sources


@router.get("/{source_id}", response_model=DataIngestionSourceResponse)
async def get_data_ingestion_source(
    source_id: str,
    organization: dict = Depends(get_current_organization),
    db: Session = Depends(get_db),
):
    """Retrieve a specific data ingestion source by ID."""
    org_id = organization["id"]

    source = db.query(DataIngestionSource).filter(
        DataIngestionSource.id == source_id,
        DataIngestionSource.organization_id == org_id,
    ).first()

    if not source:
        raise HTTPException(status_code=404, detail="Data ingestion source not found")

    logger.info(
        "data_ingestion_source_fetched",
        org_id=org_id,
        source_id=source_id[:8],
        source_name=source.name,
    )

    return source


@router.put("/{source_id}", response_model=DataIngestionSourceResponse)
async def update_data_ingestion_source(
    source_id: str,
    request: UpdateDataIngestionSourceRequest,
    organization: dict = Depends(get_current_organization),
    db: Session = Depends(get_db),
):
    """Update a data ingestion source."""
    org_id = organization["id"]

    source = db.query(DataIngestionSource).filter(
        DataIngestionSource.id == source_id,
        DataIngestionSource.organization_id == org_id,
    ).first()

    if not source:
        raise HTTPException(status_code=404, detail="Data ingestion source not found")

    # Check name uniqueness if renaming
    if request.name is not None and request.name != source.name:
        existing = db.query(DataIngestionSource).filter(
            DataIngestionSource.organization_id == org_id,
            DataIngestionSource.name == request.name,
            DataIngestionSource.id != source_id,
            DataIngestionSource.status != DataIngestionSourceStatus.DELETED,
        ).first()
        if existing:
            raise HTTPException(
                status_code=409,
                detail=f"Data ingestion source with name '{request.name}' already exists",
            )
        source.name = request.name

    if request.description is not None:
        source.description = request.description

    if request.credential_method is not None:
        source.credential_method = request.credential_method

    if request.credential_ref is not None:
        source.credential_ref = request.credential_ref

    if request.config is not None:
        source.config = request.config

    if request.schedule_type is not None:
        source.schedule_type = request.schedule_type

    if request.cron_expression is not None:
        source.cron_expression = request.cron_expression

    if request.status is not None:
        source.status = request.status

    if request.tags is not None:
        source.tags = request.tags

    db.commit()
    db.refresh(source)

    logger.info(
        "data_ingestion_source_updated",
        org_id=org_id,
        source_id=source_id[:8],
        source_name=source.name,
    )

    return source


@router.delete("/{source_id}", status_code=204)
async def delete_data_ingestion_source(
    source_id: str,
    organization: dict = Depends(get_current_organization),
    db: Session = Depends(get_db),
    hard_delete: bool = Query(False, description="Permanently delete (vs soft delete)"),
):
    """Delete a data ingestion source (soft delete by default)."""
    org_id = organization["id"]

    source = db.query(DataIngestionSource).filter(
        DataIngestionSource.id == source_id,
        DataIngestionSource.organization_id == org_id,
    ).first()

    if not source:
        raise HTTPException(status_code=404, detail="Data ingestion source not found")

    if hard_delete:
        db.delete(source)
        logger.info(
            "data_ingestion_source_hard_deleted",
            org_id=org_id,
            source_id=source_id[:8],
            source_name=source.name,
        )
    else:
        source.status = DataIngestionSourceStatus.DELETED
        logger.info(
            "data_ingestion_source_soft_deleted",
            org_id=org_id,
            source_id=source_id[:8],
            source_name=source.name,
        )

    db.commit()


@router.patch("/{source_id}/sync-status", response_model=DataIngestionSourceResponse)
async def update_sync_status(
    source_id: str,
    request: UpdateSyncStatusRequest,
    organization: dict = Depends(get_current_organization),
    db: Session = Depends(get_db),
):
    """Update sync tracking fields after a workflow starts/completes."""
    org_id = organization["id"]

    source = db.query(DataIngestionSource).filter(
        DataIngestionSource.id == source_id,
        DataIngestionSource.organization_id == org_id,
    ).first()

    if not source:
        raise HTTPException(status_code=404, detail="Data ingestion source not found")

    if request.last_sync_at is not None:
        source.last_sync_at = request.last_sync_at

    if request.last_sync_status is not None:
        source.last_sync_status = request.last_sync_status
        # Auto-update source status based on sync result
        if request.last_sync_status == "failed":
            source.status = DataIngestionSourceStatus.ERROR
        elif request.last_sync_status == "success":
            source.status = DataIngestionSourceStatus.ACTIVE

    if request.last_sync_workflow_id is not None:
        source.last_sync_workflow_id = request.last_sync_workflow_id

    if request.last_error is not None:
        source.last_error = request.last_error

    if request.increment_total_runs:
        source.total_runs = (source.total_runs or 0) + 1

    if request.increment_success:
        source.success_count = (source.success_count or 0) + 1

    if request.increment_failure:
        source.failure_count = (source.failure_count or 0) + 1

    db.commit()
    db.refresh(source)

    logger.info(
        "data_ingestion_source_sync_status_updated",
        org_id=org_id,
        source_id=source_id[:8],
        sync_status=request.last_sync_status,
    )

    return source
