import uuid

from fastapi import APIRouter, Query
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import AdminDep, PageDep, SessionDep, UserDep
from app.api.serializers import dump, dump_list
from app.core.exceptions import ValidationError
from app.core.response import paginated, success
from app.flows import get_flow
from app.models import Order
from app.repositories import PaymentRepository
from app.schemas import (
    CouponApply,
    OrderCreate,
    OrderInputUpdate,
    OrderOut,
    OrderStatusUpdate,
    PaymentOut,
)
from app.services import OrderService, PaymentService

router = APIRouter(tags=["orders"])


async def _detail(session: AsyncSession, order: Order) -> dict:
    """Order plus its payments and the flow-specific invoice summary."""
    payload = dump(OrderOut, order)
    payments = await PaymentRepository(session).list_all(order_id=order.id)
    payload["payments"] = dump_list(PaymentOut, payments)
    payload["summary"] = get_flow(order.flow).summary(order) if order.flow else {}
    return payload


@router.post("/orders")
async def create_order(payload: OrderCreate, user: UserDep, session: SessionDep) -> dict:
    order = await OrderService(session).create_draft(
        user, product_id=payload.product_id, flow_name=payload.flow, input_data=payload.input_data
    )
    return success(await _detail(session, order), "order_created")


@router.get("/orders")
async def list_orders(user: UserDep, session: SessionDep, page_params: PageDep) -> dict:
    items, total = await OrderService(session).search(
        page_params.page, page_params.size, user_id=user.id, search=page_params.search
    )
    return success(paginated(dump_list(OrderOut, items), total, page_params.page, page_params.size))


@router.get("/orders/max-affordable")
async def max_affordable(
    user: UserDep,
    session: SessionDep,
    product_id: uuid.UUID | None = Query(default=None),
    flow: str | None = Query(default=None),
) -> dict:
    value = await OrderService(session).max_affordable(user, product_id, flow)
    return success({"value": value})


@router.get("/orders/track/{order_number}")
async def track_order(order_number: str, user: UserDep, session: SessionDep) -> dict:
    order = await OrderService(session).get_by_number(order_number)
    if order.user_id != user.id:
        raise ValidationError("order_not_found")
    return success(await _detail(session, order))


@router.get("/orders/{order_id}")
async def get_order(order_id: uuid.UUID, user: UserDep, session: SessionDep) -> dict:
    order = await OrderService(session).get_for_user(order_id, user.id)
    return success(await _detail(session, order))


@router.put("/orders/{order_id}/inputs")
async def update_inputs(
    order_id: uuid.UUID, payload: OrderInputUpdate, user: UserDep, session: SessionDep
) -> dict:
    service = OrderService(session)
    order = await service.get_for_user(order_id, user.id)
    order = await service.update_inputs(order, payload.input_data)
    return success(await _detail(session, order), "updated")


@router.post("/orders/{order_id}/coupon")
async def apply_coupon(
    order_id: uuid.UUID, payload: CouponApply, user: UserDep, session: SessionDep
) -> dict:
    service = OrderService(session)
    order = await service.get_for_user(order_id, user.id)
    order = await service.apply_coupon(order, payload.code)
    return success(await _detail(session, order), "coupon_applied")


@router.delete("/orders/{order_id}/coupon")
async def remove_coupon(order_id: uuid.UUID, user: UserDep, session: SessionDep) -> dict:
    service = OrderService(session)
    order = await service.get_for_user(order_id, user.id)
    order = await service.remove_coupon(order)
    return success(await _detail(session, order), "coupon_removed")


@router.put("/orders/{order_id}/confirm")
async def confirm_order(order_id: uuid.UUID, user: UserDep, session: SessionDep) -> dict:
    service = OrderService(session)
    order = await service.get_for_user(order_id, user.id)
    order = await service.confirm(order)
    methods = await PaymentService(session).methods_for(order, user)
    return success({"order": await _detail(session, order), "payment": methods}, "order_confirmed")


@router.put("/orders/{order_id}/cancel")
async def cancel_order(order_id: uuid.UUID, user: UserDep, session: SessionDep) -> dict:
    service = OrderService(session)
    order = await service.get_for_user(order_id, user.id)
    order = await service.cancel(order, "cancelled by user")
    return success(await _detail(session, order), "order_cancelled")


# -- admin ----------------------------------------------------------
@router.get("/admin/orders")
async def admin_list_orders(
    session: SessionDep,
    _: AdminDep,
    page_params: PageDep,
    status: str | None = Query(default=None),
    payment_status: str | None = Query(default=None),
    flow: str | None = Query(default=None),
    user_id: uuid.UUID | None = Query(default=None),
) -> dict:
    items, total = await OrderService(session).search(
        page_params.page,
        page_params.size,
        status=status,
        payment_status=payment_status,
        flow=flow,
        user_id=user_id,
        search=page_params.search,
    )
    return success(paginated(dump_list(OrderOut, items), total, page_params.page, page_params.size))


@router.get("/admin/orders/{order_id}")
async def admin_get_order(order_id: uuid.UUID, session: SessionDep, _: AdminDep) -> dict:
    order = await OrderService(session).get(order_id)
    return success(await _detail(session, order))


@router.put("/admin/orders/{order_id}/status")
async def admin_update_status(
    order_id: uuid.UUID, payload: OrderStatusUpdate, session: SessionDep, _: AdminDep
) -> dict:
    service = OrderService(session)
    order = await service.get(order_id)
    note = payload.note or ""

    if payload.status == "completed":
        order = await service.complete(order, note)
    elif payload.status == "cancelled":
        order = await service.cancel(order, note)
    elif payload.status == "failed":
        order = await service.fail(order, note or "failed by admin")
    elif payload.status == "refunded":
        order = await service.refund(order, note)
    elif payload.status == "processing":
        order = await service.process(order)
    else:
        raise ValidationError("invalid_status")

    return success(await _detail(session, order), "status_updated")


@router.post("/admin/orders/{order_id}/retry")
async def admin_retry_order(order_id: uuid.UUID, session: SessionDep, _: AdminDep) -> dict:
    service = OrderService(session)
    order = await service.get(order_id)
    order = await service.process(order)
    return success(await _detail(session, order), "order_retried")
