from __future__ import annotations

from datetime import datetime
from typing import Any
from uuid import UUID

from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession

from src.api.v1.dependencies.auth import get_current_user, require_role
from src.connectors.base import ConnectorResult
from src.core.exceptions import NotFoundException, ValidationException
from src.domain.entities import ConnectorConfigEntity, ConnectorEntity, ConnectorStatus, ERPType, UserRole
from src.infrastructure.database.repositories import ConnectorConfigRepository, ConnectorRepository
from src.infrastructure.database.session import get_db_session
from src.services.auth.security import TokenData
from src.services.connector_service import ConnectorService

router = APIRouter(prefix="/connectors", tags=["Connectors"])


class ConnectorCreateRequest(BaseModel):
    erp_system_id: UUID
    pharmacy_id: UUID
    name: str = Field(..., min_length=1, max_length=255)
    connector_type: str
    version: str = "1.0.0"
    config: dict[str, Any] | None = None
    metadata: dict[str, Any] | None = None


class ConnectorUpdateRequest(BaseModel):
    name: str | None = Field(None, min_length=1, max_length=255)
    version: str | None = None
    config: dict[str, Any] | None = None
    status: str | None = None
    metadata: dict[str, Any] | None = None


class ConnectorConfigRequest(BaseModel):
    key: str = Field(..., min_length=1, max_length=100)
    value: str
    encrypted: bool = False
    description: str | None = None


class ConnectorResponse(BaseModel):
    id: UUID
    erp_system_id: UUID
    pharmacy_id: UUID
    name: str
    connector_type: str
    version: str
    config: dict[str, Any] | None
    status: str
    last_heartbeat_at: datetime | None
    last_error: str | None
    metadata: dict[str, Any] | None
    created_at: datetime | None
    updated_at: datetime | None


class ConnectorListResponse(BaseModel):
    items: list[ConnectorResponse]
    total: int
    offset: int
    limit: int


class ConnectorConfigResponse(BaseModel):
    id: UUID
    connector_id: UUID
    key: str
    value: str
    encrypted: bool
    description: str | None
    created_at: datetime | None
    updated_at: datetime | None


class ConnectorTestResponse(BaseModel):
    success: bool
    data: dict[str, Any] | None = None
    error: str | None = None


class ConnectorDeployResponse(BaseModel):
    id: UUID
    status: str
    message: str


def _entity_to_response(e: ConnectorEntity) -> ConnectorResponse:
    return ConnectorResponse(
        id=e.id, erp_system_id=e.erp_system_id, pharmacy_id=e.pharmacy_id,
        name=e.name, connector_type=e.connector_type, version=e.version,
        config=e.config, status=e.status, last_heartbeat_at=e.last_heartbeat_at,
        last_error=e.last_error, metadata=e.metadata_, created_at=e.created_at,
        updated_at=e.updated_at,
    )


def _config_entity_to_response(e: ConnectorConfigEntity) -> ConnectorConfigResponse:
    return ConnectorConfigResponse(
        id=e.id, connector_id=e.connector_id, key=e.key, value=e.value,
        encrypted=e.encrypted, description=e.description,
        created_at=e.created_at, updated_at=e.updated_at,
    )


def _get_service(session: AsyncSession) -> ConnectorService:
    return ConnectorService(
        connector_repo=ConnectorRepository(session),
        config_repo=ConnectorConfigRepository(session),
    )


@router.get("", response_model=ConnectorListResponse)
async def list_connectors(
    erp_system_id: UUID | None = None,
    pharmacy_id: UUID | None = None,
    status: str | None = None,
    offset: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=200),
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    service = _get_service(session)
    entities, total = await service.list_connectors(erp_system_id=erp_system_id, pharmacy_id=pharmacy_id, status=status, offset=offset, limit=limit)
    return ConnectorListResponse(items=[_entity_to_response(e) for e in entities], total=total, offset=offset, limit=limit)


@router.get("/types", response_model=list[str])
async def list_connector_types(
    current_user: TokenData = Depends(get_current_user),
):
    from src.connectors.registry import connector_registry
    return connector_registry.list_registered_types()


@router.get("/{connector_id}", response_model=ConnectorResponse)
async def get_connector(
    connector_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    service = _get_service(session)
    entity = await service.get_connector(connector_id)
    if not entity:
        raise NotFoundException("Connector", str(connector_id))
    return _entity_to_response(entity)


@router.post("", response_model=ConnectorResponse, status_code=201)
async def create_connector(
    body: ConnectorCreateRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    service = _get_service(session)
    entity = ConnectorEntity(
        erp_system_id=body.erp_system_id, pharmacy_id=body.pharmacy_id,
        name=body.name, connector_type=body.connector_type,
        version=body.version, config=body.config,
        status=ConnectorStatus.PENDING, metadata_=body.metadata,
    )
    created = await service.create_connector(entity)
    return _entity_to_response(created)


@router.put("/{connector_id}", response_model=ConnectorResponse)
async def update_connector(
    connector_id: UUID,
    body: ConnectorUpdateRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    service = _get_service(session)
    entity = await service.get_connector(connector_id)
    if not entity:
        raise NotFoundException("Connector", str(connector_id))

    update_data = body.model_dump(exclude_unset=True)
    if "metadata" in update_data:
        entity.metadata_ = update_data.pop("metadata")
    for key, value in update_data.items():
        setattr(entity, key, value)

    updated = await service.update_connector(entity)
    return _entity_to_response(updated)


@router.delete("/{connector_id}", status_code=204)
async def delete_connector(
    connector_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    service = _get_service(session)
    deleted = await service.delete_connector(connector_id)
    if not deleted:
        raise NotFoundException("Connector", str(connector_id))


@router.post("/{connector_id}/test", response_model=ConnectorTestResponse)
async def test_connector(
    connector_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    result = await service.test_connection(connector_id)
    return ConnectorTestResponse(success=result.success, data=result.data, error=result.error)


@router.post("/{connector_id}/deploy", response_model=ConnectorDeployResponse)
async def deploy_connector(
    connector_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    service = _get_service(session)
    entity = await service.deploy_connector(connector_id)
    return ConnectorDeployResponse(id=entity.id, status=entity.status, message="Connector deployed successfully")


@router.post("/{connector_id}/deactivate", response_model=ConnectorDeployResponse)
async def deactivate_connector(
    connector_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    service = _get_service(session)
    entity = await service.deactivate_connector(connector_id)
    return ConnectorDeployResponse(id=entity.id, status=entity.status, message="Connector deactivated successfully")


@router.get("/{connector_id}/configs", response_model=list[ConnectorConfigResponse])
async def list_connector_configs(
    connector_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    service = _get_service(session)
    connector = await service.get_connector(connector_id)
    if not connector:
        raise NotFoundException("Connector", str(connector_id))
    configs = await service.get_configs(connector_id)
    return [_config_entity_to_response(c) for c in configs]


@router.put("/{connector_id}/configs", response_model=ConnectorConfigResponse)
async def upsert_connector_config(
    connector_id: UUID,
    body: ConnectorConfigRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    service = _get_service(session)
    connector = await service.get_connector(connector_id)
    if not connector:
        raise NotFoundException("Connector", str(connector_id))
    cfg_entity = ConnectorConfigEntity(
        connector_id=connector_id, key=body.key, value=body.value,
        encrypted=body.encrypted, description=body.description,
    )
    created = await service.upsert_config(cfg_entity)
    return _config_entity_to_response(created)


@router.delete("/{connector_id}/configs/{config_id}", status_code=204)
async def delete_connector_config(
    connector_id: UUID,
    config_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    service = _get_service(session)
    await service.delete_config(config_id)
