from __future__ import annotations

import logging
import math
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
from sqlalchemy.orm import Session

from src.apps.auth.utils.auth import get_current_superuser, get_current_merchant
from src.apps.users.models.user import User
from src.apps.merchants.models.merchant import Merchant
from src.core.database import get_session
from src.apps.feature_control.schemas.feature_schemas import (
    FeatureFlagResponse,
    FeaturePlanCreate,
    FeaturePlanUpdate,
    FeaturePlanResponse,
    FeaturePlanAssignRequest,
    FeatureOverridePatchRequest,
    MerchantFeatureStateResponse,
    MerchantFeaturePlanResponse,
    MerchantFeatureOverrideResponse,
    FeatureMeResponse,
)
import src.apps.feature_control.crud as crud
from src.apps.feature_control.services import FeatureService, _get_redis

logger = logging.getLogger(__name__)

router = APIRouter()
features_me_router = APIRouter()


def _get_ip(request: Request) -> str:
    forwarded = request.headers.get("X-Forwarded-For")
    if forwarded:
        return forwarded.split(",")[-1].strip()
    return request.client.host if request.client else "unknown"


def _audit(db, user_id, action, target_type, target_id, metadata=None, ip=None):
    from src.apps.admin.models.admin_audit_log import AdminAuditLog
    AdminAuditLog.log(
        db,
        admin_user_id=user_id,
        action=action,
        target_type=target_type,
        target_id=target_id,
        metadata=metadata,
        ip_address=ip,
    )


# ─── Feature Flags Catalog ────────────────────────────────────────────────────

@router.get("/feature-flags", tags=["ADMIN_FEATURES"])
async def list_feature_flags(
    db: Session = Depends(get_session),
    current_user: User = Depends(get_current_superuser),
):
    flags = crud.get_all_active_flags(db)
    return {"data": [FeatureFlagResponse.model_validate(f) for f in flags]}


# ─── Feature Plans ────────────────────────────────────────────────────────────

@router.get("/feature-plans", tags=["ADMIN_FEATURES"])
async def list_feature_plans(
    page: int = Query(1, ge=1),
    per_page: int = Query(20, ge=1, le=100),
    is_active: Optional[bool] = Query(None),
    db: Session = Depends(get_session),
    current_user: User = Depends(get_current_superuser),
):
    plans, total = crud.get_plans(db, page=page, per_page=per_page, is_active=is_active)
    return {
        "data": [FeaturePlanResponse.model_validate(p) for p in plans],
        "total": total,
        "page": page,
        "per_page": per_page,
        "pages": math.ceil(total / per_page) if total else 1,
    }


@router.post("/feature-plans", status_code=status.HTTP_201_CREATED, tags=["ADMIN_FEATURES"])
async def create_feature_plan(
    payload: FeaturePlanCreate,
    request: Request,
    db: Session = Depends(get_session),
    current_user: User = Depends(get_current_superuser),
):
    plan = crud.create_plan(db, payload, created_by=current_user.id)
    db.commit()
    db.refresh(plan)
    _audit(db, current_user.id, "feature_plan.created", "feature_plan", plan.id,
           metadata={"name": plan.name}, ip=_get_ip(request))
    db.commit()
    return {"data": FeaturePlanResponse.model_validate(plan)}


@router.get("/feature-plans/{plan_id}", tags=["ADMIN_FEATURES"])
async def get_feature_plan(
    plan_id: int,
    db: Session = Depends(get_session),
    current_user: User = Depends(get_current_superuser),
):
    plan = crud.get_plan(db, plan_id)
    if not plan:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Feature plan not found")
    return {"data": FeaturePlanResponse.model_validate(plan)}


@router.patch("/feature-plans/{plan_id}", tags=["ADMIN_FEATURES"])
async def update_feature_plan(
    plan_id: int,
    payload: FeaturePlanUpdate,
    request: Request,
    db: Session = Depends(get_session),
    current_user: User = Depends(get_current_superuser),
    redis_client=Depends(_get_redis),
):
    plan = crud.update_plan(db, plan_id, payload)
    db.commit()
    db.refresh(plan)

    # Invalidate cache for all merchants on this plan
    merchant_ids = crud.get_active_merchant_ids_for_plan(db, plan_id)
    FeatureService.invalidate_bulk(redis_client, merchant_ids)

    _audit(db, current_user.id, "feature_plan.updated", "feature_plan", plan.id,
           metadata={"name": plan.name}, ip=_get_ip(request))
    db.commit()
    return {"data": FeaturePlanResponse.model_validate(plan)}


@router.delete("/feature-plans/{plan_id}", tags=["ADMIN_FEATURES"])
async def delete_feature_plan(
    plan_id: int,
    request: Request,
    db: Session = Depends(get_session),
    current_user: User = Depends(get_current_superuser),
):
    plan = crud.soft_delete_plan(db, plan_id)
    db.commit()
    _audit(db, current_user.id, "feature_plan.deleted", "feature_plan", plan.id,
           metadata={"name": plan.name}, ip=_get_ip(request))
    db.commit()
    return {"data": {"id": plan.id, "deleted": True}}


@router.post("/feature-plans/{plan_id}/assign", tags=["ADMIN_FEATURES"])
async def assign_feature_plan(
    plan_id: int,
    payload: FeaturePlanAssignRequest,
    request: Request,
    db: Session = Depends(get_session),
    current_user: User = Depends(get_current_superuser),
    redis_client=Depends(_get_redis),
):
    affected_ids = crud.assign_plan_to_merchants(
        db, plan_id, payload.merchant_ids, assigned_by=current_user.id, notes=payload.notes
    )
    db.commit()

    FeatureService.invalidate_bulk(redis_client, affected_ids)

    _audit(db, current_user.id, "feature_plan.assigned", "feature_plan", plan_id,
           metadata={"merchant_ids": affected_ids, "count": len(affected_ids)},
           ip=_get_ip(request))
    db.commit()
    return {"data": {"plan_id": plan_id, "assigned_to": len(affected_ids)}}


# ─── Merchant Feature State ───────────────────────────────────────────────────

@router.get("/merchants/{merchant_id}/features", tags=["ADMIN_FEATURES"])
async def get_merchant_features(
    merchant_id: int,
    db: Session = Depends(get_session),
    current_user: User = Depends(get_current_superuser),
    redis_client=Depends(_get_redis),
):
    resolved = FeatureService.resolve(db, redis_client, merchant_id)
    assignment = crud.get_active_plan_assignment(db, merchant_id)
    overrides = crud.get_overrides(db, merchant_id)

    current_plan = None
    if assignment:
        current_plan = MerchantFeaturePlanResponse(
            id=assignment.id,
            plan_id=assignment.plan_id,
            plan_name=assignment.plan.name,
            plan_description=assignment.plan.description,
            is_active=assignment.is_active,
            assigned_at=assignment.assigned_at,
            notes=assignment.notes,
        )

    return {
        "data": MerchantFeatureStateResponse(
            current_plan=current_plan,
            resolved_features=resolved,
            overrides=[MerchantFeatureOverrideResponse.model_validate(o) for o in overrides],
        )
    }


@router.patch("/merchants/{merchant_id}/features/overrides", tags=["ADMIN_FEATURES"])
async def set_merchant_feature_overrides(
    merchant_id: int,
    payload: FeatureOverridePatchRequest,
    request: Request,
    db: Session = Depends(get_session),
    current_user: User = Depends(get_current_superuser),
    redis_client=Depends(_get_redis),
):
    overrides = crud.upsert_overrides(db, merchant_id, payload.overrides, set_by=current_user.id)
    db.commit()

    FeatureService.invalidate(redis_client, merchant_id)

    _audit(
        db, current_user.id, "feature_override.set", "merchant", merchant_id,
        metadata={"slugs": [o.feature_slug for o in payload.overrides]},
        ip=_get_ip(request),
    )
    db.commit()
    return {
        "data": [MerchantFeatureOverrideResponse.model_validate(o) for o in overrides]
    }


# ─── Merchant Portal: GET /features/me ───────────────────────────────────────

@features_me_router.get("/features/me", tags=["FEATURES"])
async def get_my_features(
    db: Session = Depends(get_session),
    merchant: Merchant = Depends(get_current_merchant),
    redis_client=Depends(_get_redis),
):
    features = FeatureService.resolve(db, redis_client, merchant.id)
    from src.apps.plan_management.crud import get_active_plan_for_merchant
    plan_info = get_active_plan_for_merchant(db, merchant.id)
    return {"data": FeatureMeResponse(
        features=features,
        plan_name=plan_info["plan_name"] if plan_info else None,
        plan_id=plan_info["plan_id"] if plan_info else None,
    )}
