from fastapi import FastAPI, Request, Depends
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.templating import Jinja2Templates
from app.core.database import init_db, get_db
from app.routes import (
    auth, customers, list_customers, technician, settings, voucher_agents,
    vouchers, payments, users, billing, devices, customer_management, reports,
    ops, service_packages, shop,
)
from app.core.security import get_current_user
from app.models.models import User
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import func, select
import uvicorn
import os
import asyncio
from dotenv import load_dotenv
from app.core.status_monitor import monitor_loop, voucher_monitor_loop

app = FastAPI(title="BestWeb Billing")
status_monitor_task = None
voucher_monitor_task = None
app.add_middleware(GZipMiddleware, minimum_size=500, compresslevel=5)

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
load_dotenv(os.path.join(BASE_DIR, ".env"))
load_dotenv(os.path.join(BASE_DIR, "app", ".env"), override=False)
app.mount("/static", StaticFiles(directory=os.path.join(BASE_DIR, "static")), name="static")
templates = Jinja2Templates(directory=os.path.join(BASE_DIR, "app", "templates"))

for router in (
    auth.router, customers.router, list_customers.router, technician.router,
    settings.router, voucher_agents.router, vouchers.router, payments.router,
    users.router, billing.router, devices.router, customer_management.router,
    reports.router, ops.router, service_packages.router, shop.router,
):
    app.include_router(router)

# Admin katalog/pesanan online. Terpisah dari router publik di atas supaya
# dependensi role hanya berlaku pada prefix /shop/orders.
app.include_router(shop.admin_router)


@app.on_event("startup")
async def on_startup():
    await init_db()
    global status_monitor_task, voucher_monitor_task
    status_monitor_task = asyncio.create_task(monitor_loop())
    voucher_monitor_task = asyncio.create_task(voucher_monitor_loop())


@app.on_event("shutdown")
async def on_shutdown():
    global status_monitor_task, voucher_monitor_task
    for task in (status_monitor_task, voucher_monitor_task):
        if task:
            task.cancel()
            try:
                await task
            except asyncio.CancelledError:
                pass
    status_monitor_task = None
    voucher_monitor_task = None


@app.get("/", response_class=HTMLResponse)
async def home(
    request: Request,
    db: AsyncSession = Depends(get_db),
):
    # Root domain dipakai katalog publik, dashboard internal pindah ke /admin.
    # Resolve user secara manual (bukan lewat get_current_user) karena helper
    # itu melempar redirect ke /login — di sini tamu justru harus diberi katalog,
    # bukan login form.
    user = None
    cookie_id = request.cookies.get("user_id")
    if cookie_id and cookie_id.isdigit():
        user = await db.scalar(select(User).where(User.id == int(cookie_id), User.is_active == True))
    if user is None:
        return RedirectResponse(url="/shop", status_code=303)
    from app.models.models import Customer, Voucher, VoucherBatch
    visible = Customer.is_archived == False
    async def count(*conditions):
        return await db.scalar(select(func.count(Customer.id)).where(visible, *conditions)) or 0

    total = await count()
    pending = await count(Customer.status.in_(("request", "pending_provisioning")))
    installing = await count(Customer.status == "installing")
    configuring = await count(Customer.status == "configuring")
    active = await count(Customer.status == "active")
    los = await count(Customer.olt_provisioning_status == "los")
    dying_gasp = await count(Customer.olt_provisioning_status == "dying_gasp")
    offline = await count(Customer.olt_provisioning_status == "offline")
    waiting = await count(Customer.olt_provisioning_status == "provisioned", Customer.mikrotik_provisioning_status == "waiting_session")
    online = await count(Customer.olt_provisioning_status == "provisioned")

    total_vouchers = await db.scalar(select(func.count(Voucher.id))) or 0
    available_vouchers = await db.scalar(select(func.count(Voucher.id)).where(Voucher.status == "available")) or 0
    sold_vouchers = await db.scalar(select(func.count(Voucher.id)).where(Voucher.status == "sold")) or 0
    total_batches = await db.scalar(select(func.count(VoucherBatch.id))) or 0

    return templates.TemplateResponse(
        request=request,
        name="index.html",
        context={
            "title": "BestWeb Billing System",
            "message": "Sistem Manajemen ISP: Bulanan PPP & Hotspot Voucher.",
            "current_user": user,
            "stats": {
                "total": total, "pending": pending, "installing": installing,
                "configuring": configuring, "active": active,
                "los": los, "dying_gasp": dying_gasp, "offline": offline,
                "waiting": waiting, "online": online,
                "total_vouchers": total_vouchers,
                "available_vouchers": available_vouchers,
                "sold_vouchers": sold_vouchers,
                "total_batches": total_batches,
            },
        },
    )

@app.get("/api/dashboard/stats")
async def api_dashboard_stats(db: AsyncSession = Depends(get_db)):
    from app.models.models import Customer, Voucher, VoucherBatch
    visible = Customer.is_archived == False
    async def count(*conditions):
        return await db.scalar(select(func.count(Customer.id)).where(visible, *conditions)) or 0
    
    total = await count()
    pending = await count(Customer.status.in_(("request", "pending_provisioning")))
    installing = await count(Customer.status == "installing")
    configuring = await count(Customer.status == "configuring")
    active = await count(Customer.status == "active")
    los = await count(Customer.olt_provisioning_status == "los")
    dying_gasp = await count(Customer.olt_provisioning_status == "dying_gasp")
    offline = await count(Customer.olt_provisioning_status == "offline")
    waiting = await count(Customer.olt_provisioning_status == "provisioned", Customer.mikrotik_provisioning_status == "waiting_session")
    online = await count(Customer.olt_provisioning_status == "provisioned")

    total_vouchers = await db.scalar(select(func.count(Voucher.id))) or 0
    available_vouchers = await db.scalar(select(func.count(Voucher.id)).where(Voucher.status == "available")) or 0
    sold_vouchers = await db.scalar(select(func.count(Voucher.id)).where(Voucher.status == "sold")) or 0
    total_batches = await db.scalar(select(func.count(VoucherBatch.id))) or 0
    
    return {
        "total": total, "pending": pending, "installing": installing,
        "configuring": configuring, "active": active,
        "los": los, "dying_gasp": dying_gasp, "offline": offline,
        "waiting": waiting, "online": online,
        "total_vouchers": total_vouchers,
        "available_vouchers": available_vouchers,
        "sold_vouchers": sold_vouchers,
        "total_batches": total_batches
    }
if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
