import logging
import json
from datetime import datetime
from uuid import uuid4
from typing import Any, Dict, List, Optional

from fastapi import APIRouter, HTTPException, Query, Request
from pydantic import BaseModel, Field

from app.modules.orders.backend.economic_export import ordre_economic_export_service
from app.modules.orders.backend.service import aggregate_order_lines

logger = logging.getLogger(__name__)
router = APIRouter()
ALLOWED_SYNC_STATUSES = {"pending", "exported", "failed", "posted", "paid"}


class OrdreLineInput(BaseModel):
    line_key: str
    source_type: str
    source_id: Optional[int] = None
    description: str
    quantity: float = Field(gt=0)
    unit_price: float = Field(ge=0)
    discount_percentage: float = Field(default=0, ge=0, le=100)
    unit: Optional[str] = None
    product_id: Optional[int] = None
    selected: bool = True


class OrdreExportRequest(BaseModel):
    customer_id: int
    lines: List[OrdreLineInput]
    notes: Optional[str] = None
    layout_number: Optional[int] = None
    draft_id: Optional[int] = None
    force_export: bool = False


class OrdreDraftUpsertRequest(BaseModel):
    title: str = Field(min_length=1, max_length=120)
    customer_id: Optional[int] = None
    lines: List[Dict[str, Any]] = Field(default_factory=list)
    notes: Optional[str] = None
    layout_number: Optional[int] = None


class OrdreDraftConsolidateRequest(BaseModel):
    draft_ids: List[int] = Field(..., min_length=2)


def _safe_json_field(value: Any) -> Any:
    if value is None:
        return None
    if isinstance(value, (dict, list)):
        return value
    if isinstance(value, str):
        try:
            return json.loads(value)
        except json.JSONDecodeError:
            return value
    return value


def _get_user_id_from_request(http_request: Request) -> Optional[int]:
    state_user_id = getattr(http_request.state, "user_id", None)
    if state_user_id is None:
        return None
    try:
        return int(state_user_id)
    except (TypeError, ValueError):
        return None


def _log_sync_event(
    draft_id: int,
    event_type: str,
    from_status: Optional[str],
    to_status: Optional[str],
    event_payload: Dict[str, Any],
    user_id: Optional[int],
) -> None:
    """Best-effort logging of sync events for ordre_drafts."""
    try:
        from app.core.database import execute_query

        execute_query(
            """
            INSERT INTO ordre_draft_sync_events (
                draft_id,
                event_type,
                from_status,
                to_status,
                event_payload,
                created_by_user_id
            ) VALUES (%s, %s, %s, %s, %s::jsonb, %s)
            """,
            (
                draft_id,
                event_type,
                from_status,
                to_status,
                json.dumps(event_payload, ensure_ascii=False),
                user_id,
            )
        )
    except Exception as e:
        logger.warning("⚠️ Could not log ordre sync event for draft %s: %s", draft_id, e)


@router.get("/ordre/aggregate")
async def get_ordre_aggregate(
    customer_id: Optional[int] = Query(None),
    sag_id: Optional[int] = Query(None),
    q: Optional[str] = Query(None),
):
    """Aggregate global ordre lines from subscriptions, hardware and sales."""
    try:
        return aggregate_order_lines(customer_id=customer_id, sag_id=sag_id, q=q)
    except Exception as e:
        logger.error("❌ Error aggregating ordre lines: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to aggregate ordre lines")


@router.get("/ordre/config")
async def get_ordre_config():
    """Return ordre module safety config for frontend banner."""
    return {
        "economic_read_only": ordre_economic_export_service.read_only,
        "economic_dry_run": ordre_economic_export_service.dry_run,
        "default_layout": ordre_economic_export_service.default_layout,
        "default_product": ordre_economic_export_service.default_product,
    }


@router.post("/ordre/export")
async def export_ordre(request: OrdreExportRequest, http_request: Request):
    """Export selected ordre lines to e-conomic draft order."""
    try:
        user_id = _get_user_id_from_request(http_request)
        previous_status = None
        export_idempotency_key = None

        if request.draft_id:
            from app.core.database import execute_query_single

            draft_row = execute_query_single(
                """
                SELECT id, sync_status, export_idempotency_key, export_status_json
                FROM ordre_drafts
                WHERE id = %s
                """,
                (request.draft_id,)
            )
            if not draft_row:
                raise HTTPException(status_code=404, detail="Draft not found")

            previous_status = (draft_row.get("sync_status") or "pending").strip().lower()
            if previous_status in {"exported", "posted", "paid"} and not request.force_export:
                raise HTTPException(
                    status_code=409,
                    detail=f"Draft already exported with status '{previous_status}'. Use force_export=true to retry.",
                )

            export_idempotency_key = draft_row.get("export_idempotency_key") or str(uuid4())
            _log_sync_event(
                request.draft_id,
                "export_attempt",
                previous_status,
                previous_status,
                {"force_export": request.force_export, "idempotency_key": export_idempotency_key},
                user_id,
            )

        line_payload = [line.model_dump() for line in request.lines]
        export_result = await ordre_economic_export_service.export_order(
            customer_id=request.customer_id,
            lines=line_payload,
            notes=request.notes,
            layout_number=request.layout_number,
            user_id=user_id,
        )

        exported_line_keys = [line.get("line_key") for line in line_payload if line.get("line_key")]
        export_result["exported_line_keys"] = exported_line_keys

        if request.draft_id:
            from app.core.database import execute_query_single, execute_query

            existing = execute_query_single("SELECT export_status_json FROM ordre_drafts WHERE id = %s", (request.draft_id,))
            existing_status = _safe_json_field((existing or {}).get("export_status_json")) or {}
            if not isinstance(existing_status, dict):
                existing_status = {}

            line_status = "dry-run" if export_result.get("dry_run") else "exported"
            for line_key in exported_line_keys:
                existing_status[line_key] = {
                    "status": line_status,
                    "timestamp": datetime.utcnow().isoformat(),
                }

            economic_order_number = (
                export_result.get("economic_order_number")
                or export_result.get("order_number")
                or export_result.get("orderNumber")
            )
            economic_invoice_number = (
                export_result.get("economic_invoice_number")
                or export_result.get("invoice_number")
                or export_result.get("invoiceNumber")
            )
            target_sync_status = "pending" if export_result.get("dry_run") else "exported"

            execute_query(
                """
                UPDATE ordre_drafts
                SET export_status_json = %s::jsonb,
                    sync_status = %s,
                    export_idempotency_key = %s,
                    economic_order_number = COALESCE(%s, economic_order_number),
                    economic_invoice_number = COALESCE(%s, economic_invoice_number),
                    last_sync_at = CURRENT_TIMESTAMP,
                    last_exported_at = CURRENT_TIMESTAMP,
                    updated_at = CURRENT_TIMESTAMP
                WHERE id = %s
                """,
                (
                    json.dumps(existing_status, ensure_ascii=False),
                    target_sync_status,
                    export_idempotency_key,
                    str(economic_order_number) if economic_order_number is not None else None,
                    str(economic_invoice_number) if economic_invoice_number is not None else None,
                    request.draft_id,
                ),
            )

            _log_sync_event(
                request.draft_id,
                "export_success",
                previous_status,
                target_sync_status,
                {
                    "dry_run": bool(export_result.get("dry_run")),
                    "idempotency_key": export_idempotency_key,
                    "economic_order_number": economic_order_number,
                    "economic_invoice_number": economic_invoice_number,
                },
                user_id,
            )

        return export_result
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error exporting ordre to e-conomic: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to export ordre")


@router.get("/ordre/drafts")
async def list_ordre_drafts(
    http_request: Request, 
    limit: int = Query(25, ge=1, le=100)
):
    """List all ordre drafts (no user filtering)."""
    try:
        query = """
               SELECT ordre_drafts.id, ordre_drafts.title, ordre_drafts.customer_id, ordre_drafts.notes, ordre_drafts.layout_number, ordre_drafts.created_by_user_id,
                 coverage_start, coverage_end, billing_direction, source_subscription_ids,
                 invoice_aggregate_key, sync_status, export_idempotency_key,
                 economic_order_number, economic_invoice_number,
                 ev_latest.event_type AS latest_event_type,
                 ev_latest.created_at AS latest_event_at,
                 (
                    SELECT COUNT(*)
                    FROM ordre_draft_sync_events ev
                    WHERE ev.draft_id = ordre_drafts.id
                 ) AS sync_event_count,
                 ordre_drafts.last_sync_at, ordre_drafts.created_at, ordre_drafts.updated_at, ordre_drafts.last_exported_at
            FROM ordre_drafts
            LEFT JOIN LATERAL (
                SELECT ordre_draft_sync_events.event_type, ordre_draft_sync_events.created_at
                FROM ordre_draft_sync_events
                WHERE draft_id = ordre_drafts.id
                ORDER BY ordre_draft_sync_events.created_at DESC, ordre_draft_sync_events.id DESC
                LIMIT 1
            ) ev_latest ON TRUE
            ORDER BY updated_at DESC, id DESC
            LIMIT %s
        """
        params = (limit,)

        from app.core.database import execute_query
        return execute_query(query, params) or []
    except Exception as e:
        logger.error("❌ Error listing ordre drafts: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to list ordre drafts")


@router.get("/ordre/drafts/{draft_id}")
async def get_ordre_draft(draft_id: int, http_request: Request):
    """Get single ordre draft with lines payload (no user filtering)."""
    try:
        query = "SELECT * FROM ordre_drafts WHERE id = %s LIMIT 1"
        params = (draft_id,)

        from app.core.database import execute_query_single
        draft = execute_query_single(query, params)
        if not draft:
            raise HTTPException(status_code=404, detail="Draft not found")

        draft["lines_json"] = _safe_json_field(draft.get("lines_json")) or []
        draft["export_status_json"] = _safe_json_field(draft.get("export_status_json")) or {}
        return draft
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error fetching ordre draft: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to fetch ordre draft")


@router.post("/ordre/drafts")
async def create_ordre_draft(request: OrdreDraftUpsertRequest, http_request: Request):
    """Create a new ordre draft."""
    try:
        user_id = _get_user_id_from_request(http_request)
        from app.core.database import execute_query

        query = """
            INSERT INTO ordre_drafts (
                title,
                customer_id,
                lines_json,
                notes,
                layout_number,
                created_by_user_id,
                sync_status,
                export_status_json,
                updated_at
            ) VALUES (%s, %s, %s::jsonb, %s, %s, %s, 'pending', %s::jsonb, CURRENT_TIMESTAMP)
            RETURNING *
        """
        params = (
            request.title,
            request.customer_id,
            json.dumps(request.lines, ensure_ascii=False),
            request.notes,
            request.layout_number,
            user_id,
            json.dumps({}, ensure_ascii=False),
        )
        result = execute_query(query, params)
        return result[0]
    except Exception as e:
        logger.error("❌ Error creating ordre draft: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to create ordre draft")


@router.get("/ordre/drafts/sync-status/summary")
async def get_ordre_draft_sync_summary(http_request: Request):
    """Return sync status counters for ordre drafts."""
    try:
        from app.core.database import execute_query_single

        query = """
            SELECT
                COUNT(*) FILTER (WHERE sync_status = 'pending') AS pending_count,
                COUNT(*) FILTER (WHERE sync_status = 'exported') AS exported_count,
                COUNT(*) FILTER (WHERE sync_status = 'failed') AS failed_count,
                COUNT(*) FILTER (WHERE sync_status = 'posted') AS posted_count,
                COUNT(*) FILTER (WHERE sync_status = 'paid') AS paid_count,
                COUNT(*) AS total_count
            FROM ordre_drafts
        """
        return execute_query_single(query, ()) or {
            "pending_count": 0,
            "exported_count": 0,
            "failed_count": 0,
            "posted_count": 0,
            "paid_count": 0,
            "total_count": 0,
        }
    except Exception as e:
        logger.error("❌ Error loading ordre sync summary: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to load sync summary")


@router.patch("/ordre/drafts/{draft_id}/sync-status")
async def update_ordre_draft_sync_status(draft_id: int, payload: Dict[str, Any], http_request: Request):
    """Update sync lifecycle fields for one ordre draft."""
    try:
        user_id = _get_user_id_from_request(http_request)
        sync_status = (payload.get("sync_status") or "").strip().lower()
        if sync_status not in ALLOWED_SYNC_STATUSES:
            raise HTTPException(status_code=400, detail="Invalid sync_status")

        economic_order_number = payload.get("economic_order_number")
        economic_invoice_number = payload.get("economic_invoice_number")
        export_status_json = payload.get("export_status_json")

        updates = ["sync_status = %s", "last_sync_at = CURRENT_TIMESTAMP", "updated_at = CURRENT_TIMESTAMP"]
        values: List[Any] = [sync_status]

        if economic_order_number is not None:
            updates.append("economic_order_number = %s")
            values.append(str(economic_order_number) if economic_order_number else None)

        if economic_invoice_number is not None:
            updates.append("economic_invoice_number = %s")
            values.append(str(economic_invoice_number) if economic_invoice_number else None)

        if export_status_json is not None:
            updates.append("export_status_json = %s::jsonb")
            values.append(json.dumps(export_status_json, ensure_ascii=False))

        if sync_status in {"exported", "posted", "paid"}:
            updates.append("last_exported_at = CURRENT_TIMESTAMP")

        from app.core.database import execute_query_single
        previous = execute_query_single(
            "SELECT sync_status FROM ordre_drafts WHERE id = %s",
            (draft_id,)
        )
        if not previous:
            raise HTTPException(status_code=404, detail="Draft not found")
        from_status = (previous.get("sync_status") or "pending").strip().lower()

        values.append(draft_id)
        from app.core.database import execute_query
        result = execute_query(
            f"""
            UPDATE ordre_drafts
            SET {', '.join(updates)}
            WHERE id = %s
            RETURNING *
            """,
            tuple(values)
        )
        if not result:
            raise HTTPException(status_code=404, detail="Draft not found")

        _log_sync_event(
            draft_id,
            "sync_status_manual_update",
            from_status,
            sync_status,
            {
                "economic_order_number": economic_order_number,
                "economic_invoice_number": economic_invoice_number,
            },
            user_id,
        )
        return result[0]
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error updating ordre draft sync status: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to update draft sync status")


@router.get("/ordre/drafts/{draft_id}/sync-events")
async def list_ordre_draft_sync_events(
    draft_id: int,
    http_request: Request,
    limit: int = Query(100, ge=1, le=500),
    offset: int = Query(0, ge=0),
    event_type: Optional[str] = Query(None),
    from_status: Optional[str] = Query(None),
    to_status: Optional[str] = Query(None),
    from_date: Optional[str] = Query(None),
    to_date: Optional[str] = Query(None),
):
    """List audit events for one ordre draft sync lifecycle."""
    try:
        from app.core.database import execute_query

        where_clauses = ["draft_id = %s"]
        params: List[Any] = [draft_id]

        if event_type:
            where_clauses.append("event_type = %s")
            params.append(event_type)
        if from_status:
            where_clauses.append("from_status = %s")
            params.append(from_status)
        if to_status:
            where_clauses.append("to_status = %s")
            params.append(to_status)
        if from_date:
            where_clauses.append("created_at >= %s::timestamp")
            params.append(from_date)
        if to_date:
            where_clauses.append("created_at <= %s::timestamp")
            params.append(to_date)

        count_query = f"""
            SELECT COUNT(*) AS total
            FROM ordre_draft_sync_events
            WHERE {' AND '.join(where_clauses)}
        """
        total_row = execute_query(count_query, tuple(params)) or [{"total": 0}]
        total = int(total_row[0].get("total") or 0)

        data_query = f"""
            SELECT id, draft_id, event_type, from_status, to_status, event_payload, created_by_user_id, created_at
            FROM ordre_draft_sync_events
            WHERE {' AND '.join(where_clauses)}
            ORDER BY created_at DESC, id DESC
            LIMIT %s OFFSET %s
            """
        params.extend([limit, offset])
        rows = execute_query(data_query, tuple(params)) or []

        return {
            "items": rows,
            "total": total,
            "limit": limit,
            "offset": offset,
        }
    except Exception as e:
        logger.error("❌ Error listing ordre draft sync events: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to list sync events")


@router.patch("/ordre/drafts/{draft_id}")
async def update_ordre_draft(draft_id: int, request: OrdreDraftUpsertRequest, http_request: Request):
    """Update existing ordre draft."""
    try:
        from app.core.database import execute_query

        query = """
            UPDATE ordre_drafts
            SET title = %s,
                customer_id = %s,
                lines_json = %s::jsonb,
                notes = %s,
                layout_number = %s,
                updated_at = CURRENT_TIMESTAMP
            WHERE id = %s
            RETURNING *
        """
        params = (
            request.title,
            request.customer_id,
            json.dumps(request.lines, ensure_ascii=False),
            request.notes,
            request.layout_number,
            draft_id,
        )

        result = execute_query(query, params)
        if not result:
            raise HTTPException(status_code=404, detail="Draft not found")
        return result[0]
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error updating ordre draft: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to update ordre draft")


@router.delete("/ordre/drafts/{draft_id}")
async def delete_ordre_draft(draft_id: int, http_request: Request):
    """Delete ordre draft."""
    try:
        from app.core.database import execute_query

        query = "DELETE FROM ordre_drafts WHERE id = %s RETURNING id"
        params = (draft_id,)

        result = execute_query(query, params)
        if not result:
            raise HTTPException(status_code=404, detail="Draft not found")
        return {"status": "deleted", "id": draft_id}
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error deleting ordre draft: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to delete ordre draft")


@router.post("/ordre/drafts/consolidate")
async def consolidate_ordre_drafts(payload: OrdreDraftConsolidateRequest, http_request: Request):
    """Consolidate two or more drafts for the same customer into one draft."""
    try:
        draft_ids = sorted(set(int(x) for x in payload.draft_ids if int(x) > 0))
        if len(draft_ids) < 2:
            raise HTTPException(status_code=400, detail="Select at least two drafts to consolidate")

        placeholders = ",".join(["%s"] * len(draft_ids))
        from app.core.database import execute_query, execute_query_single

        rows = execute_query(
            f"""
            SELECT *
            FROM ordre_drafts
            WHERE id IN ({placeholders})
            ORDER BY id ASC
            """,
            tuple(draft_ids),
        ) or []

        if len(rows) != len(draft_ids):
            raise HTTPException(status_code=404, detail="One or more drafts were not found")

        customer_ids = {row.get("customer_id") for row in rows}
        if len(customer_ids) != 1:
            raise HTTPException(status_code=400, detail="Drafts must belong to the same customer")

        customer_id = next(iter(customer_ids))
        if customer_id is None:
            raise HTTPException(status_code=400, detail="Drafts without customer_id cannot be consolidated")

        blocked_statuses = {"exported", "posted", "paid"}
        for row in rows:
            sync_status = str(row.get("sync_status") or "pending").strip().lower()
            if sync_status in blocked_statuses:
                raise HTTPException(
                    status_code=400,
                    detail=f"Draft #{row.get('id')} has status '{sync_status}' and cannot be consolidated",
                )

        primary = rows[0]
        secondary_ids = [int(row["id"]) for row in rows[1:]]

        merged_lines: List[Dict[str, Any]] = []
        for row in rows:
            lines = _safe_json_field(row.get("lines_json")) or []
            if isinstance(lines, list):
                merged_lines.extend(lines)

        notes_parts = [str(row.get("notes")).strip() for row in rows if row.get("notes")]
        merged_notes = "\n\n".join(part for part in notes_parts if part)

        aggregate_key = primary.get("invoice_aggregate_key") or f"consolidated-customer-{customer_id}"
        keep_id = int(primary["id"])

        execute_query(
            """
            UPDATE ordre_drafts
            SET lines_json = %s::jsonb,
                notes = %s,
                invoice_aggregate_key = %s,
                updated_at = CURRENT_TIMESTAMP
            WHERE id = %s
            """,
            (
                json.dumps(merged_lines, ensure_ascii=False),
                merged_notes or None,
                aggregate_key,
                keep_id,
            ),
        )

        if secondary_ids:
            delete_placeholders = ",".join(["%s"] * len(secondary_ids))
            execute_query(
                f"DELETE FROM ordre_drafts WHERE id IN ({delete_placeholders})",
                tuple(secondary_ids),
            )

        updated = execute_query_single("SELECT * FROM ordre_drafts WHERE id = %s", (keep_id,))

        _log_sync_event(
            keep_id,
            "drafts_consolidated",
            primary.get("sync_status"),
            primary.get("sync_status"),
            {
                "merged_from": draft_ids,
                "kept_id": keep_id,
                "customer_id": customer_id,
                "line_count": len(merged_lines),
            },
            _get_user_id_from_request(http_request),
        )

        return {
            "success": True,
            "kept_draft_id": keep_id,
            "merged_draft_ids": draft_ids,
            "removed_draft_ids": secondary_ids,
            "line_count": len(merged_lines),
            "draft": updated,
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error("❌ Error consolidating ordre drafts: %s", e, exc_info=True)
        raise HTTPException(status_code=500, detail="Failed to consolidate ordre drafts")
