"""Application service for EDA agent CRUD and state mapping."""
from __future__ import annotations

from datetime import datetime
from typing import List
import uuid

from fastapi import HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from eda_agent.models.persistence import AgentProfile, AgentStateSnapshot
from eda_agent.schemas import (
    AgentCreateRequest,
    AgentListItemResponse,
    AgentStateResponse,
    AgentSummaryResponse,
    AgentUpdateRequest,
)
from eda_agent.services.field_service import FieldService
from eda_agent.state import EDAState, ValueWeight


class AgentService:
    """Service layer for profile and snapshot lifecycle."""

    def __init__(self, field_service: FieldService | None = None):
        self.field_service = field_service or FieldService()

    async def get_profile_and_snapshot(
        self, db: AsyncSession, agent_id: uuid.UUID
    ) -> tuple[AgentProfile, AgentStateSnapshot]:
        profile = (
            await db.execute(select(AgentProfile).where(AgentProfile.id == agent_id))
        ).scalar_one_or_none()
        if not profile:
            raise HTTPException(status_code=404, detail=f"Agent {agent_id} not found")

        snapshot = (
            await db.execute(
                select(AgentStateSnapshot).where(AgentStateSnapshot.agent_id == agent_id)
            )
        ).scalar_one_or_none()
        if not snapshot:
            raise HTTPException(
                status_code=500, detail=f"State snapshot missing for agent {agent_id}"
            )
        return profile, snapshot

    def state_from_snapshot(
        self, profile: AgentProfile, snapshot: AgentStateSnapshot
    ) -> EDAState:
        payload = {
            "agent_id": str(profile.id),
            "agent_role": profile.agent_role,
            "agent_name": profile.agent_name,
            "beliefs": snapshot.beliefs or {},
            "norm_bindings": snapshot.norm_bindings or [],
            "value_weights": snapshot.value_weights or [],
            "active_fields": snapshot.active_fields or [],
            "current_intention": snapshot.current_intention,
            "trust_score": snapshot.trust_score,
            "created_at": snapshot.created_at.isoformat(),
            "updated_at": snapshot.updated_at.isoformat(),
        }
        return EDAState.from_dict(payload)

    def state_to_response(
        self, state: EDAState, profile: AgentProfile, snapshot: AgentStateSnapshot
    ) -> AgentStateResponse:
        data = state.to_dict()
        return AgentStateResponse(
            id=profile.id,
            agent_name=data.get("agent_name") or profile.agent_name,
            agent_role=data["agent_role"],
            agent_description=profile.agent_description or "",
            org_id=profile.org_id,
            beliefs=data["beliefs"],
            norm_bindings=data["norm_bindings"],
            value_weights=data["value_weights"],
            active_fields=data["active_fields"],
            current_intention=data["current_intention"],
            trust_score=data["trust_score"],
            created_at=snapshot.created_at,
            updated_at=snapshot.updated_at,
        )

    def profile_snapshot_to_list_item(
        self, profile: AgentProfile, snapshot: AgentStateSnapshot | None
    ) -> AgentListItemResponse:
        beliefs = snapshot.beliefs if snapshot and isinstance(snapshot.beliefs, dict) else {}
        norm_bindings = (
            snapshot.norm_bindings if snapshot and isinstance(snapshot.norm_bindings, list) else []
        )
        value_weights = (
            snapshot.value_weights if snapshot and isinstance(snapshot.value_weights, list) else []
        )
        active_fields = (
            snapshot.active_fields if snapshot and isinstance(snapshot.active_fields, list) else []
        )
        current_intention = snapshot.current_intention if snapshot else None
        trust_score = float(snapshot.trust_score) if snapshot else 0.8
        created_at = snapshot.created_at if snapshot else profile.created_at
        updated_at = snapshot.updated_at if snapshot else profile.updated_at
        return AgentListItemResponse(
            id=profile.id,
            agent_name=profile.agent_name,
            agent_role=profile.agent_role,
            agent_description=profile.agent_description or "",
            org_id=profile.org_id,
            beliefs=beliefs,
            norm_bindings=norm_bindings,
            value_weights=value_weights,
            active_fields=active_fields,
            current_intention=current_intention,
            trust_score=trust_score,
            created_at=created_at,
            updated_at=updated_at,
        )

    async def create_agent(self, db: AsyncSession, request: AgentCreateRequest) -> AgentSummaryResponse:
        exists = (
            await db.execute(select(AgentProfile).where(AgentProfile.agent_name == request.agent_name))
        ).scalar_one_or_none()
        if exists:
            raise HTTPException(status_code=409, detail=f"Agent {request.agent_name} already exists")

        if "active_fields" in request.model_fields_set:
            resolved_active_fields = await self.field_service.validate_active_fields(
                db, request.active_fields
            )
        else:
            resolved_active_fields = await self.field_service.resolve_active_fields(
                db, request.active_fields
            )

        profile = AgentProfile(
            agent_name=request.agent_name,
            agent_role=request.agent_role,
            agent_description=request.agent_description or "",
            org_id=request.org_id,
        )
        db.add(profile)

        await db.flush()
        value_weights = [
            ValueWeight(
                value_name=i.value_name,
                weight=i.weight,
                hierarchy=i.hierarchy,
                rationale=i.rationale,
            )
            for i in request.value_weights
        ]
        initial_state = EDAState(
            agent_id=str(profile.id),
            agent_role=request.agent_role,
            agent_name=request.agent_name,
            value_weights=value_weights,
            active_fields=resolved_active_fields,
            trust_score=request.trust_score,
        )
        state_dict = initial_state.to_dict()
        snapshot = AgentStateSnapshot(
            agent_id=profile.id,
            beliefs=state_dict["beliefs"],
            norm_bindings=state_dict["norm_bindings"],
            value_weights=state_dict["value_weights"],
            active_fields=state_dict["active_fields"],
            current_intention=initial_state.current_intention,
            trust_score=initial_state.trust_score,
        )
        db.add(snapshot)
        await db.commit()
        await db.refresh(profile)
        return AgentSummaryResponse.model_validate(profile)

    async def list_agents(
        self, db: AsyncSession, *, limit: int
    ) -> List[AgentListItemResponse]:
        rows = (
            await db.execute(
                select(AgentProfile, AgentStateSnapshot)
                .join(
                    AgentStateSnapshot,
                    AgentStateSnapshot.agent_id == AgentProfile.id,
                    isouter=True,
                )
                .order_by(AgentProfile.updated_at.desc())
                .limit(limit)
            )
        ).all()
        return [
            self.profile_snapshot_to_list_item(profile, snapshot)
            for profile, snapshot in rows
        ]

    async def get_agent_state(
        self, db: AsyncSession, *, agent_id: uuid.UUID
    ) -> AgentStateResponse:
        profile, snapshot = await self.get_profile_and_snapshot(db, agent_id)
        state = self.state_from_snapshot(profile, snapshot)
        return self.state_to_response(state, profile, snapshot)

    async def update_agent(
        self,
        db: AsyncSession,
        *,
        agent_id: uuid.UUID,
        request: AgentUpdateRequest,
    ) -> AgentSummaryResponse:
        profile, snapshot = await self.get_profile_and_snapshot(db, agent_id)
        update_data = request.model_dump(exclude_unset=True)

        if "agent_role" in update_data:
            profile.agent_role = update_data["agent_role"]
        if "agent_description" in update_data and update_data["agent_description"] is not None:
            profile.agent_description = update_data["agent_description"]
        if "org_id" in update_data:
            profile.org_id = update_data["org_id"]

        if "active_fields" in update_data:
            snapshot.active_fields = await self.field_service.validate_active_fields(
                db, update_data["active_fields"]
            )
        if "trust_score" in update_data and update_data["trust_score"] is not None:
            snapshot.trust_score = update_data["trust_score"]

        profile.updated_at = datetime.now()
        snapshot.updated_at = datetime.now()
        await db.commit()
        await db.refresh(profile)
        return AgentSummaryResponse.model_validate(profile)

    async def delete_agent(self, db: AsyncSession, *, agent_id: uuid.UUID) -> None:
        profile, _snapshot = await self.get_profile_and_snapshot(db, agent_id)
        await db.delete(profile)
        await db.commit()
