"""
BMC Hub - FastAPI Application
Main application entry point
"""

import logging
from pathlib import Path
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import RedirectResponse
from contextlib import asynccontextmanager

from app.core.config import settings
from app.core.database import init_db
from app.core.auth_service import AuthService
from app.core.database import execute_query_single


_users_column_cache: dict[str, bool] = {}


def _users_column_exists(column_name: str) -> bool:
    if column_name in _users_column_cache:
        return _users_column_cache[column_name]

    result = execute_query_single(
        """
        SELECT 1
        FROM information_schema.columns
        WHERE table_schema = 'public'
          AND table_name = 'users'
          AND column_name = %s
        LIMIT 1
        """,
        (column_name,),
    )
    exists = bool(result)
    _users_column_cache[column_name] = exists
    return exists


def _get_mission_access_pin() -> str:
    row = execute_query_single("SELECT value FROM settings WHERE key = %s", ("mission_access_pin",))
    db_pin = str((row or {}).get("value") or "").strip()
    env_pin = str(getattr(settings, "MISSION_ACCESS_PIN", "") or "").strip()
    return db_pin or env_pin


def _has_valid_mission_pin_token(request: Request) -> bool:
    token = request.cookies.get("mission_pin_token")
    payload = AuthService.verify_token(token) if token else None
    return bool(payload and payload.get("scope") == "mission_pin")

def get_version():
    """Read version from VERSION file"""
    try:
        version_file = Path(__file__).parent / "VERSION"
        return version_file.read_text().strip()
    except Exception:
        return "unknown"

# Import Feature Routers
from app.customers.backend import router as customers_api
from app.customers.backend import views as customers_views
from app.customers.backend import bmc_office_router
# from app.hardware.backend import router as hardware_api  # Replaced by hardware module
from app.alert_notes.backend import router as alert_notes_api
from app.billing.backend import router as billing_api
from app.billing.frontend import views as billing_views
from app.economy.backend import router as economy_api
from app.economy.frontend import views as economy_views
from app.system.backend import router as system_api
from app.system.backend import sync_router
from app.dashboard.backend import views as dashboard_views
from app.dashboard.backend import router as dashboard_api
from app.dashboard.backend import mission_router as mission_api
from app.prepaid.backend import router as prepaid_api
from app.prepaid.backend import views as prepaid_views
from app.fixed_price.backend import router as fixed_price_api
from app.fixed_price.frontend import views as fixed_price_views
from app.subscriptions.backend import router as subscriptions_api
from app.subscriptions.frontend import views as subscriptions_views
from app.products.backend import router as products_api
from app.products.frontend import views as products_views
from app.ticket.backend import router as ticket_api
from app.ticket.frontend import views as ticket_views
from app.vendors.backend import router as vendors_api
from app.vendors.backend import views as vendors_views
from app.timetracking.backend import router as timetracking_api
from app.timetracking.frontend import views as timetracking_views
from app.contacts.backend import views as contacts_views
from app.contacts.backend import router_simple as contacts_api
from app.tags.backend import router as tags_api
from app.tags.backend import views as tags_views
from app.emails.backend import router as emails_api
from app.emails.frontend import views as emails_views
from app.settings.backend import router as settings_api
from app.settings.backend import email_templates as email_templates_api
from app.settings.backend import views as settings_views
from app.backups.backend.router import router as backups_api
from app.backups.frontend import views as backups_views
from app.backups.backend.scheduler import backup_scheduler
from app.conversations.backend import router as conversations_api
from app.conversations.frontend import views as conversations_views
from app.opportunities.backend import router as opportunities_api
from app.opportunities.frontend import views as opportunities_views
from app.auth.backend import router as auth_api
from app.auth.backend import views as auth_views
from app.auth.backend import admin as auth_admin_api
from app.devportal.backend import router as devportal_api
from app.devportal.backend import views as devportal_views
from app.routers import anydesk
from app.anydesk.backend import views as anydesk_views

# Modules
from app.modules.webshop.backend import router as webshop_api
from app.modules.webshop.frontend import views as webshop_views
from app.modules.sag.backend import router as sag_api
from app.modules.sag.backend import reminders as sag_reminders_api
from app.modules.sag.frontend import views as sag_views
from app.modules.hardware.backend import router as hardware_module_api
from app.modules.hardware.frontend import views as hardware_module_views
from app.modules.locations.backend import router as locations_api
from app.modules.locations.frontend import views as locations_views
from app.modules.nextcloud.backend import router as nextcloud_api
from app.modules.search.backend import router as search_api
from app.modules.wiki.backend import router as wiki_api
from app.fixed_price.backend import router as fixed_price_api
from app.modules.telefoni.backend import router as telefoni_api
from app.modules.telefoni.frontend import views as telefoni_views
from app.modules.calendar.backend import router as calendar_api
from app.modules.calendar.frontend import views as calendar_views
from app.modules.orders.backend import router as orders_api
from app.modules.orders.frontend import views as orders_views
from app.modules.fedex.backend import router as fedex_api
from app.modules.fedex.frontend import views as fedex_views
from app.modules.manual.backend import router as manual_api
from app.modules.manual.frontend import views as manual_views
from app.modules.bottom_bar.backend import router as bottom_bar_api
from app.modules.bottom_bar.backend import public_router as bottom_bar_public_api
from app.modules.rentals.backend import router as rentals_api
from app.modules.task_templates.backend import router as task_templates_api
from app.bug_reports.backend import router as bug_reports_api

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler('logs/app.log')
    ]
)

logger = logging.getLogger(__name__)

@asynccontextmanager
async def lifespan(app: FastAPI):
    """Lifecycle management - startup and shutdown"""
    # Startup
    logger.info("🚀 Starting BMC Hub...")
    logger.info(f"Database: {settings.DATABASE_URL}")
    
    init_db()
    
    # Start unified scheduler (handles backups + email fetch + reminders)
    backup_scheduler.start()
    
    # Register reminder scheduler job
    from app.jobs.check_reminders import check_reminders
    from apscheduler.triggers.interval import IntervalTrigger
    from apscheduler.triggers.cron import CronTrigger
    
    backup_scheduler.scheduler.add_job(
        func=check_reminders,
        trigger=IntervalTrigger(minutes=5),
        id='check_reminders',
        name='Check Reminders',
        max_instances=1,
        replace_existing=True
    )
    logger.info("✅ Reminder job scheduled (every 5 minutes)")

    # Register subscription invoice processing job
    from app.jobs.process_subscriptions import process_subscriptions
    
    backup_scheduler.scheduler.add_job(
        func=process_subscriptions,
        trigger=CronTrigger(hour=4, minute=0),
        id='process_subscriptions',
        name='Process Subscription Invoices',
        max_instances=1,
        replace_existing=True
    )
    logger.info("✅ Subscription invoice job scheduled (daily at 04:00)")

    # Register ordre draft sync reconcile job
    from app.jobs.reconcile_ordre_drafts import reconcile_ordre_drafts_sync_status

    backup_scheduler.scheduler.add_job(
        func=reconcile_ordre_drafts_sync_status,
        trigger=CronTrigger(hour=4, minute=30),
        kwargs={"apply_changes": True},
        id='reconcile_ordre_drafts_sync_status',
        name='Reconcile Ordre Draft Sync Status',
        max_instances=1,
        replace_existing=True
    )
    logger.info("✅ Ordre draft reconcile job scheduled (daily at 04:30)")

    if settings.ESET_ENABLED and settings.ESET_SYNC_ENABLED:
        from app.jobs.eset_sync import run_eset_sync

        backup_scheduler.scheduler.add_job(
            func=run_eset_sync,
            trigger=IntervalTrigger(minutes=settings.ESET_SYNC_INTERVAL_MINUTES),
            id='eset_sync',
            name='ESET Sync',
            max_instances=1,
            replace_existing=True
        )
        logger.info("✅ ESET sync job scheduled (every %d minutes)", settings.ESET_SYNC_INTERVAL_MINUTES)

    if settings.ANYDESK_LOCAL_SYNC_ENABLED:
        from app.jobs.anydesk_local_sync import sync_anydesk_local_sessions

        backup_scheduler.scheduler.add_job(
            func=sync_anydesk_local_sessions,
            trigger=IntervalTrigger(minutes=settings.ANYDESK_LOCAL_SYNC_INTERVAL_MINUTES),
            id='anydesk_local_sync',
            name='AnyDesk Local Sessions Sync',
            max_instances=1,
            replace_existing=True
        )
        logger.info(
            "✅ AnyDesk local sync job scheduled (every %d minutes)",
            settings.ANYDESK_LOCAL_SYNC_INTERVAL_MINUTES,
        )

    if settings.LINKS_MODULE_ENABLED and settings.LINKS_DEAD_LINK_CHECK_ENABLED:
        from app.modules.links.jobs.dead_link_check import check_links_health

        backup_scheduler.scheduler.add_job(
            func=check_links_health,
            trigger=IntervalTrigger(minutes=settings.LINKS_DEAD_LINK_CHECK_INTERVAL_MINUTES),
            id='check_links_health',
            name='Check Links Health',
            max_instances=1,
            replace_existing=True
        )
        logger.info("✅ Links health job scheduled (every %d minutes)", settings.LINKS_DEAD_LINK_CHECK_INTERVAL_MINUTES)
    
    logger.info("✅ System initialized successfully")
    yield
    # Shutdown
    backup_scheduler.stop()
    logger.info("👋 Shutting down...")

# Create FastAPI app
app = FastAPI(
    title="BMC Hub API",
    description="""
    Central management system for BMC Networks.
    
    **Key Features:**
    - Customer management
    - Hardware tracking
    - Service management
    - Billing integration
    """,
    version="1.0.0",
    lifespan=lifespan,
    docs_url="/api/docs",
    redoc_url="/api/redoc",
    openapi_url="/api/openapi.json"
)

# CORS middleware - use CORS_ORIGINS if set, otherwise fallback to ALLOWED_ORIGINS
cors_origins = settings.CORS_ORIGINS.split(",") if settings.CORS_ORIGINS else settings.ALLOWED_ORIGINS
app.add_middleware(
    CORSMiddleware,
    allow_origins=cors_origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Global auth middleware
@app.middleware("http")
async def auth_middleware(request: Request, call_next):
    path = request.url.path
    mission_path = path.startswith("/dashboard/mission-control") or path.startswith("/api/v1/mission/")
    mission_pin = _get_mission_access_pin() if mission_path else ""

    public_paths = {
        "/health",
        "/login",
        "/api/v1/auth/login",
        "/mission/pin",
        "/mission/pin/verify",
        "/mission/pin/logout",
    }

    public_prefixes = {
        "/api/v1/mission/webhook/telefoni/",
        "/api/v1/mission/webhook/uptime",
        "/api/v1/mission/webhook/camera/",
        "/api/v1/mission/webhook/environment/",
    }

    # Yealink Action URL callbacks (secured inside telefoni module by token/IP)
    public_paths.add("/api/v1/telefoni/established")
    public_paths.add("/api/v1/telefoni/terminated")
    public_paths.add("/api/v1/mission/webhook/telefoni/ringing")
    public_paths.add("/api/v1/mission/webhook/telefoni/answered")
    public_paths.add("/api/v1/mission/webhook/telefoni/hangup")
    public_paths.add("/api/v1/mission/webhook/uptime")

    if settings.DEV_ALLOW_ARCHIVED_IMPORT:
        public_paths.add("/api/v1/ticket/archived/simply/import")
        public_paths.add("/api/v1/ticket/archived/simply/modules")
        public_paths.add("/api/v1/ticket/archived/simply/ticket")
        public_paths.add("/api/v1/ticket/archived/simply/record")

    if (
        path in public_paths
        or path.startswith("/mission/pin")
        or any(path.startswith(prefix) for prefix in public_prefixes)
        or path.startswith("/static")
        or path.startswith("/docs")
    ):
        return await call_next(request)

    if mission_path and _has_valid_mission_pin_token(request):
        return await call_next(request)

    token = None
    auth_header = request.headers.get("Authorization")
    if auth_header and auth_header.lower().startswith("bearer "):
        token = auth_header.split(" ", 1)[1]
    else:
        token = request.cookies.get("access_token")

    payload = AuthService.verify_token(token) if token else None
    if not payload:
        if mission_path:
            if path.startswith("/api"):
                from fastapi.responses import JSONResponse
                return JSONResponse(
                    status_code=401,
                    content={
                        "detail": "Mission PIN required" if mission_pin else "Mission PIN not configured"
                    }
                )
            return RedirectResponse(url="/mission/pin", status_code=302)

        if path.startswith("/api"):
            from fastapi.responses import JSONResponse
            return JSONResponse(
                status_code=401,
                content={"detail": "Not authenticated"}
            )
        return RedirectResponse(url="/login")

    user_id_value = payload.get("sub") or payload.get("user_id")
    if user_id_value is not None:
        try:
            request.state.user_id = int(user_id_value)
        except (TypeError, ValueError):
            request.state.user_id = None

    if path.startswith("/api") and not payload.get("shadow_admin"):
        sub_value = payload.get("sub")
        if not sub_value:
            from fastapi.responses import JSONResponse
            return JSONResponse(
                status_code=401,
                content={"detail": "Invalid token"}
            )
        try:
            user_id = int(sub_value)
        except (TypeError, ValueError):
            from fastapi.responses import JSONResponse
            return JSONResponse(
                status_code=401,
                content={"detail": "Invalid token"}
            )

        if _users_column_exists("is_2fa_enabled"):
            user = execute_query_single(
                "SELECT COALESCE(is_2fa_enabled, FALSE) AS is_2fa_enabled FROM users WHERE user_id = %s",
                (user_id,),
            )
            is_2fa_enabled = bool(user and user.get("is_2fa_enabled"))
        else:
            # Older schemas without 2FA columns should not block authenticated requests.
            is_2fa_enabled = False

        if not is_2fa_enabled:
            allowed_2fa_paths = (
                "/api/v1/auth/2fa",
                "/api/v1/auth/me",
                "/api/v1/auth/logout"
            )
            if not path.startswith(allowed_2fa_paths):
                from fastapi.responses import JSONResponse
                return JSONResponse(
                    status_code=403,
                    content={"detail": "2FA required"}
                )

    return await call_next(request)

# Include routers
app.include_router(customers_api.router, prefix="/api/v1", tags=["Customers"])
app.include_router(bmc_office_router.router, prefix="/api/v1", tags=["BMC Office"])
# app.include_router(hardware_api.router, prefix="/api/v1", tags=["Hardware"])  # Replaced by hardware module
app.include_router(alert_notes_api, prefix="/api/v1", tags=["Alert Notes"])
app.include_router(billing_api.router, prefix="/api/v1", tags=["Billing"])
app.include_router(economy_api.router, prefix="/api/v1", tags=["Economy"])
app.include_router(system_api.router, prefix="/api/v1", tags=["System"])
app.include_router(dashboard_api.router, prefix="/api/v1", tags=["Dashboard"])
app.include_router(mission_api.router, prefix="/api/v1", tags=["Mission"])
app.include_router(sync_router.router, prefix="/api/v1/system", tags=["System Sync"])
app.include_router(prepaid_api.router, prefix="/api/v1", tags=["Prepaid Cards"])
app.include_router(fixed_price_api.router, prefix="/api/v1", tags=["Fixed-Price Agreements"])
app.include_router(subscriptions_api.router, prefix="/api/v1", tags=["Subscriptions"])
app.include_router(products_api.router, prefix="/api/v1", tags=["Products"])
app.include_router(ticket_api.router, prefix="/api/v1/ticket", tags=["Tickets"])
app.include_router(vendors_api.router, prefix="/api/v1", tags=["Vendors"])
app.include_router(contacts_api.router, prefix="/api/v1", tags=["Contacts"])
app.include_router(timetracking_api, prefix="/api/v1", tags=["Time Tracking"])
app.include_router(tags_api.router, prefix="/api/v1", tags=["Tags"])
app.include_router(emails_api.router, prefix="/api/v1", tags=["Emails"])
app.include_router(settings_api.router, prefix="/api/v1", tags=["Settings"])
app.include_router(email_templates_api.router, prefix="/api/v1", tags=["Email Templates"])
app.include_router(backups_api, prefix="/api/v1", tags=["Backups"])
app.include_router(conversations_api.router, prefix="/api/v1", tags=["Conversations"])
app.include_router(opportunities_api.router, prefix="/api/v1", tags=["Opportunities"])
app.include_router(auth_api.router, prefix="/api/v1/auth", tags=["Auth"])
app.include_router(auth_admin_api.router, prefix="/api/v1", tags=["Auth Admin"])
app.include_router(anydesk.router, prefix="/api/v1", tags=["Remote Support"])
app.include_router(bug_reports_api.router, prefix="/api/v1", tags=["Bug Reports"])

# Module Routers
app.include_router(webshop_api.router, prefix="/api/v1", tags=["Webshop"])
app.include_router(sag_api.router, prefix="/api/v1", tags=["Cases"])
app.include_router(sag_reminders_api.router, tags=["Reminders"])  # No prefix - endpoints have full path
app.include_router(hardware_module_api.router, prefix="/api/v1", tags=["Hardware Module"])
app.include_router(locations_api, prefix="/api/v1", tags=["Locations"])
app.include_router(nextcloud_api.router, prefix="/api/v1/nextcloud", tags=["Nextcloud"])
app.include_router(search_api.router, prefix="/api/v1", tags=["Search"])
app.include_router(wiki_api.router, prefix="/api/v1/wiki", tags=["Wiki"])
app.include_router(devportal_api.router, prefix="/api/v1/devportal", tags=["Devportal"])
app.include_router(telefoni_api.router, prefix="/api/v1", tags=["Telefoni"])
app.include_router(calendar_api.router, prefix="/api/v1", tags=["Calendar"])
app.include_router(orders_api.router, prefix="/api/v1", tags=["Orders"])
app.include_router(fedex_api.router, prefix="/api/v1", tags=["FedEx"])
app.include_router(manual_api.router, prefix="/api/v1", tags=["Manual"])
app.include_router(bottom_bar_api.router, prefix="/api/v1/bottom-bar", tags=["Bottom Bar"])
app.include_router(bottom_bar_public_api.router, tags=["Bottom Bar Public"])
app.include_router(rentals_api.router, prefix="/api/v1", tags=["Assets Rental Billing"])
app.include_router(task_templates_api.router, prefix="/api/v1", tags=["Task Templates"])

if settings.LINKS_MODULE_ENABLED:
    from app.modules.links.backend import router as links_api
    app.include_router(links_api.router, prefix="/api/v1", tags=["Links"])

# Frontend Routers
app.include_router(dashboard_views.router, tags=["Frontend"])
app.include_router(customers_views.router, tags=["Frontend"])
app.include_router(prepaid_views.router, tags=["Frontend"])
app.include_router(fixed_price_views.router, tags=["Frontend"])
app.include_router(subscriptions_views.router, tags=["Frontend"])
app.include_router(products_views.router, tags=["Frontend"])
app.include_router(vendors_views.router, tags=["Frontend"])
app.include_router(timetracking_views.router, tags=["Frontend"])
app.include_router(billing_views.router, tags=["Frontend"])
app.include_router(economy_views.router, tags=["Frontend"])
app.include_router(ticket_views.router, prefix="/ticket", tags=["Frontend"])
app.include_router(contacts_views.router, tags=["Frontend"])
app.include_router(tags_views.router, tags=["Frontend"])
app.include_router(settings_views.router, tags=["Frontend"])
app.include_router(emails_views.router, tags=["Frontend"])
app.include_router(backups_views.router, tags=["Frontend"])
app.include_router(conversations_views.router, tags=["Frontend"])
app.include_router(webshop_views.router, tags=["Frontend"])
app.include_router(opportunities_views.router, tags=["Frontend"])
app.include_router(auth_views.router, tags=["Frontend"])
app.include_router(sag_views.router, tags=["Frontend"])
app.include_router(hardware_module_views.router, tags=["Frontend"])
app.include_router(locations_views.router, tags=["Frontend"])
app.include_router(devportal_views.router, tags=["Frontend"])
app.include_router(telefoni_views.router, tags=["Frontend"])
app.include_router(calendar_views.router, tags=["Frontend"])
app.include_router(orders_views.router, tags=["Frontend"])
app.include_router(fedex_views.router, tags=["Frontend"])
app.include_router(anydesk_views.router, tags=["Frontend"])
app.include_router(manual_views.router, tags=["Frontend"])

if settings.LINKS_MODULE_ENABLED:
    from app.modules.links.frontend import views as links_views
    app.include_router(links_views.router, tags=["Frontend"])

# Serve static files (UI)
app.mount("/static", StaticFiles(directory="static", html=True), name="static")
app.mount("/docs", StaticFiles(directory="docs"), name="docs")

@app.get("/health")
async def health_check():
    """Health check endpoint"""
    return {
        "status": "healthy",
        "service": "BMC Hub",
        "version": get_version()
    }

if __name__ == "__main__":
    import uvicorn
    import os
    
    # Only enable reload in local development (not in Docker) - check both variables
    enable_reload = (
        os.getenv("ENABLE_RELOAD", "false").lower() == "true" or 
        os.getenv("API_RELOAD", "false").lower() == "true"
    )
    
    if enable_reload:
        uvicorn.run(
            "main:app",
            host="0.0.0.0",
            port=8000,
            reload=True,
            reload_includes=["*.py"],
            reload_dirs=["app"],
            reload_excludes=[".git/*", "*.pyc", "__pycache__/*", "logs/*", "uploads/*", "data/*"],
            log_level="info"
        )
    else:
        api_workers_raw = os.getenv("API_WORKERS", "1").strip()
        try:
            api_workers = max(1, int(api_workers_raw))
        except ValueError:
            api_workers = 1

        uvicorn.run(
            "main:app",
            host="0.0.0.0",
            port=8000,
            reload=False,
            workers=api_workers,
            timeout_keep_alive=65,
            access_log=True,
            log_level="info"
        )
