import os
import time
from pathlib import Path
from threading import Event, Lock, Thread
from typing import Optional

try:
    from dotenv import load_dotenv
except Exception:
    load_dotenv = None

if load_dotenv:
    load_dotenv()

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware

from model.order_model import OrderModel
from model.action_log_model import ActionLogModel
from utils.auth_helper import get_current_actor_optional
from utils.audit_helper import parse_request_body, outcome_from_status

from controller.admin.product_controller import router as product_router
from controller.admin.upload_controller import router as upload_router
from controller.admin.category_controller import router as category_admin_router
from controller.public.category_controller import router as category_public_router
from controller.public.product_controller import router as product_public_router
from controller.topup_controller import router as topup_router
from controller import ticket_controller
from controller.staff_controller import router as staff_router
from controller.banner import router as banner_router
from controller.import_controller import router as import_router
from controller.order_controller import router as order_controller
from controller.product_catalog_controller import router as product_catalog_router
from controller.action_log_controller import router as action_log_router
from controller.upload_public_controller import router as upload_public_router
from controller import user_controller, admin_controller
from controller.tier_settings_controller import router as tier_settings_router
from controller.blog_controller import router as blog_router
from controller.auth_controller import router as auth_router


# ============================================================
# APP CONFIG
# ============================================================

app = FastAPI(title="Ecommerce API", root_path="/pod-api-clone")
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts=["*"])

WRITE_METHODS = {"POST", "PUT", "DELETE", "PATCH"}
ALLOWED_LOG_ROLES = {"admin", "staff"}


# ============================================================
# ABSOLUTE STATIC DIRECTORIES
# ============================================================

BASE_DIR = Path(__file__).resolve().parent

UPLOADS_DIR = BASE_DIR / "uploads"
DESIGN_DIR = BASE_DIR / "design"

UPLOADS_DIR.mkdir(parents=True, exist_ok=True)
DESIGN_DIR.mkdir(parents=True, exist_ok=True)

print(f"[STATIC] BASE_DIR={BASE_DIR}")
print(f"[STATIC] UPLOADS_DIR={UPLOADS_DIR} exists={UPLOADS_DIR.exists()}")
print(f"[STATIC] DESIGN_DIR={DESIGN_DIR} exists={DESIGN_DIR.exists()}")


# ============================================================
# BACKGROUND SHIPPING SYNC
# ============================================================

_shipping_stop_event = Event()
_shipping_thread: Optional[Thread] = None
_shipping_sync_lock = Lock()


def _env_bool(name: str, default: bool = False) -> bool:
    value = str(os.getenv(name, str(int(default))) or "").strip().lower()
    return value in {"1", "true", "yes", "y", "on"}


def _env_int(name: str, default: int) -> int:
    try:
        return int(str(os.getenv(name, default)).strip())
    except Exception:
        return default


def run_shipping_sync_job() -> None:
    if not _shipping_sync_lock.acquire(blocking=False):
        print("[UPS PLAYWRIGHT SYNC] skipped overlapping run")
        return

    try:
        limit = max(1, _env_int("SHIPPING_SYNC_LIMIT", 20))
        min_age_seconds = max(0, _env_int("SHIPPING_SYNC_MIN_AGE_SECONDS", 300))

        result = OrderModel.sync_shipped_orders_to_delivered(
            limit=limit,
            min_age_seconds=min_age_seconds,
        )

        print(
            "[UPS PLAYWRIGHT SYNC] "
            f"checked={result.get('checked', 0)} "
            f"updated={result.get('updated', 0)} "
            f"limit={limit} "
            f"min_age_seconds={min_age_seconds}"
        )
    except Exception as exc:
        print(f"[UPS SHIPPING SYNC ERROR] {exc}")
    finally:
        _shipping_sync_lock.release()


def shipping_sync_loop() -> None:
    interval_seconds = max(60, _env_int("SHIPPING_SYNC_INTERVAL_SECONDS", 300))
    startup_delay_seconds = max(0, _env_int("SHIPPING_SYNC_STARTUP_DELAY_SECONDS", 15))
    enabled = False

    print(
        "[UPS PLAYWRIGHT SYNC] "
        f"enabled={enabled} "
        f"interval_seconds={interval_seconds} "
        f"startup_delay_seconds={startup_delay_seconds}"
    )

    return

    if startup_delay_seconds > 0:
        if _shipping_stop_event.wait(startup_delay_seconds):
            return

    while not _shipping_stop_event.is_set():
        run_shipping_sync_job()
        if _shipping_stop_event.wait(interval_seconds):
            break


@app.on_event("startup")
def startup_event() -> None:
    global _shipping_thread

    _shipping_stop_event.clear()

    if _shipping_thread and _shipping_thread.is_alive():
        return

    _shipping_thread = Thread(
        target=shipping_sync_loop,
        name="ups-shipping-sync-thread",
        daemon=True,
    )
    _shipping_thread.start()


@app.on_event("shutdown")
def shutdown_event() -> None:
    _shipping_stop_event.set()


# ============================================================
# ACTION LOG MIDDLEWARE
# ============================================================

@app.middleware("http")
async def action_log_middleware(request: Request, call_next):
    if request.method not in WRITE_METHODS:
        return await call_next(request)

    start_time = time.time()
    body = await request.body()
    content_type = request.headers.get("content-type", "")

    async def receive():
        return {
            "type": "http.request",
            "body": body,
            "more_body": False,
        }

    request = Request(request.scope, receive)
    actor = await get_current_actor_optional(request)
    status_code = 500

    try:
        response = await call_next(request)
        status_code = response.status_code
        return response
    finally:
        duration_ms = int((time.time() - start_time) * 1000)
        required_permission = getattr(request.state, "required_permission", None)
        final_actor = getattr(request.state, "actor", actor)

        should_log = (
            final_actor
            and str(final_actor.get("role", "")).lower() in ALLOWED_LOG_ROLES
            and str(final_actor.get("id", "")).strip() != ""
        )

        if should_log:
            try:
                ActionLogModel.write_log(
                    {
                        "method": request.method,
                        "path": request.url.path,
                        "query": str(request.url.query),
                        "statusCode": status_code,
                        "allowed": status_code not in [401, 403],
                        "success": 200 <= status_code < 300,
                        "outcome": outcome_from_status(status_code),
                        "requiredPermission": required_permission,
                        "actorId": final_actor.get("id"),
                        "actorName": final_actor.get("name"),
                        "actorRole": final_actor.get("role"),
                        "ip": request.client.host if request.client else None,
                        "userAgent": request.headers.get("user-agent"),
                        "durationMs": duration_ms,
                        "body": parse_request_body(content_type, body),
                    }
                )
            except Exception as log_error:
                print(f"[ACTION LOG ERROR] {log_error}")


# ============================================================
# CORS
# ============================================================

origins = [
    "http://103.90.225.225",
    "http://shipping.05factory.com",
    "https://shipping.05factory.com",
    "http://05factory.com",
    "https://05factory.com",
    "http://www.05factory.com",
    "https://www.05factory.com",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# ============================================================
# STATIC FILES
# Put static mounts before routers to make static paths explicit.
# ============================================================

app.mount(
    "/uploads",
    StaticFiles(directory=str(UPLOADS_DIR)),
    name="uploads",
)

app.mount(
    "/pod-api-clone/uploads",
    StaticFiles(directory=str(UPLOADS_DIR)),
    name="pod_api_clone_uploads",
)

app.mount(
    "/design",
    StaticFiles(directory=str(DESIGN_DIR)),
    name="design",
)

app.mount(
    "/pod-api-clone/design",
    StaticFiles(directory=str(DESIGN_DIR)),
    name="pod_api_clone_design",
)


# ============================================================
# ROUTERS
# ============================================================

app.include_router(staff_router, prefix="/api", tags=["Admin Staff Management"])
app.include_router(product_router, prefix="/api/admin", tags=["Admin Product"])
app.include_router(upload_router, prefix="/api/admin", tags=["Admin Upload"])
app.include_router(category_admin_router, prefix="/api/admin", tags=["Admin Category"])
app.include_router(action_log_router, prefix="/api")

app.include_router(category_public_router, prefix="/api", tags=["Public Catalog"])
app.include_router(product_public_router, prefix="/api", tags=["Public Product"])
app.include_router(topup_router, prefix="/api/topups", tags=["Top-up"])
app.include_router(ticket_controller.router, prefix="/api/tickets", tags=["Tickets"])
app.include_router(banner_router, prefix="/api", tags=["Banner"])
app.include_router(import_router, prefix="/api/import", tags=["Import Orders"])
app.include_router(order_controller, prefix="/api/orders", tags=["Orders"])
app.include_router(product_catalog_router, prefix="/api", tags=["Product Catalog"])

app.include_router(auth_router, prefix="/api")
app.include_router(user_controller.router, prefix="/api")
app.include_router(admin_controller.router, prefix="/api")
app.include_router(tier_settings_router, prefix="/api")
app.include_router(upload_public_router, prefix="/api", tags=["Public Upload"])
app.include_router(blog_router, prefix="/api", tags=["Blogs"])
