import logging
import re
from datetime import date, datetime, timedelta, timezone
from typing import Any, Dict, List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, status
from pydantic import BaseModel, Field
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from database import get_db
from models import HostingService
from crypto import encrypt_string, decrypt_string
from integrations.whm import WHMIntegration

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/hosting", tags=["hosting"])


# ---------------------------------------------------------------------------
# Schemas
# ---------------------------------------------------------------------------

class HostingBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=255)
    provider: str = Field("cPanel", max_length=64)
    ip_address: Optional[str] = None
    server_hostname: Optional[str] = None
    login_url: Optional[str] = None
    expiry_date: Optional[date] = None
    billing_cycle: str = Field("yearly", max_length=32)
    cost: Optional[int] = None
    currency: str = Field("USD", max_length=8)
    auto_renew: bool = False
    notes: Optional[str] = None


class HostingCreate(HostingBase):
    pass


class HostingUpdate(BaseModel):
    name: Optional[str] = None
    provider: Optional[str] = None
    ip_address: Optional[str] = None
    server_hostname: Optional[str] = None
    login_url: Optional[str] = None
    expiry_date: Optional[date] = None
    billing_cycle: Optional[str] = None
    cost: Optional[int] = None
    currency: Optional[str] = None
    auto_renew: Optional[bool] = None
    notes: Optional[str] = None
    status: Optional[str] = None


class HostingResponse(BaseModel):
    id: int
    name: str
    provider: str
    ip_address: Optional[str] = None
    server_hostname: Optional[str] = None
    login_url: Optional[str] = None
    cpanel_user: Optional[str] = None
    expiry_date: Optional[date] = None
    billing_cycle: str
    cost: Optional[int] = None
    currency: str
    auto_renew: bool
    status: str
    days_until_expiry: Optional[int] = None
    notes: Optional[str] = None
    created_at: datetime
    updated_at: datetime

    class Config:
        from_attributes = True


class WHMSyncRequest(BaseModel):
    host: str = Field(..., description="WHM Hostname or IP e.g. 37.27.71.8 or staging1.oneterminal.org")
    username: str = Field(..., description="WHM Username e.g. root or reseller user")
    api_token: str = Field(..., description="WHM API Token or password")
    import_to_db: bool = Field(True, description="Save fetched hosting accounts into DB")


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def calculate_hosting_status(expiry_date: Optional[date], explicit_status: Optional[str] = None) -> tuple[str, Optional[int]]:
    """Calculate status and days until expiry."""
    if explicit_status and explicit_status in ("suspended", "cancelled"):
        return explicit_status, None

    if not expiry_date:
        return "active", None

    today = date.today()
    days_left = (expiry_date - today).days

    if days_left < 0:
        return "expired", days_left
    elif days_left <= 30:
        return "expiring_soon", days_left
    else:
        return "active", days_left


def extract_cpanel_user(notes: Optional[str]) -> Optional[str]:
    """Extract cPanel User username from decrypted notes text if present."""
    if not notes:
        return None
    match = re.search(r"cPanel User:\s*([^\s\n]+)", notes, re.IGNORECASE)
    if match:
        return match.group(1).strip()
    return None


def format_hosting_response(service: HostingService) -> HostingResponse:
    """Format ORM model into Pydantic schema with decrypted notes and extracted cPanel User."""
    computed_status, days_left = calculate_hosting_status(service.expiry_date, service.status)

    decrypted_notes = None
    if service.notes_encrypted:
        try:
            decrypted_notes = decrypt_string(service.notes_encrypted)
        except Exception:
            decrypted_notes = service.notes_encrypted

    cp_user = extract_cpanel_user(decrypted_notes)

    return HostingResponse(
        id=service.id,
        name=service.name,
        provider=service.provider,
        ip_address=service.ip_address,
        server_hostname=service.server_hostname,
        login_url=service.login_url,
        cpanel_user=cp_user,
        expiry_date=service.expiry_date,
        billing_cycle=service.billing_cycle,
        cost=service.cost,
        currency=service.currency,
        auto_renew=service.auto_renew,
        status=computed_status,
        days_until_expiry=days_left,
        notes=decrypted_notes,
        created_at=service.created_at,
        updated_at=service.updated_at,
    )


# ---------------------------------------------------------------------------
# WHM API Endpoints
# ---------------------------------------------------------------------------

@router.post("/whm/test")
async def test_whm_connection(payload: WHMSyncRequest):
    """Test WHM API credentials and connectivity."""
    whm = WHMIntegration({"host": payload.host, "username": payload.username, "api_token": payload.api_token})
    is_valid = await whm.test_connection()
    if not is_valid:
        raise HTTPException(
            status_code=400,
            detail="Failed to connect to WHM API. Please check Host, Username, Port (2087), and API Token.",
        )
    return {"status": "success", "message": "Successfully connected to WHM API!"}


@router.post("/whm/sync")
async def sync_whm_accounts(payload: WHMSyncRequest, db: AsyncSession = Depends(get_db)):
    """Fetch cPanel accounts from WHM API v1 (listaccts) and save into hosting_services."""
    whm = WHMIntegration({"host": payload.host, "username": payload.username, "api_token": payload.api_token})

    try:
        accts = await whm.list_accounts()
    except Exception as exc:
        logger.error("WHM listaccts error: %s", exc)
        raise HTTPException(status_code=502, detail=f"WHM API Error: {str(exc)}")

    imported_services = []

    if payload.import_to_db:
        for acct in accts:
            domain_name = acct.get("domain_name")
            if not domain_name:
                continue

            stmt = select(HostingService).where(HostingService.name == domain_name)
            res = await db.execute(stmt)
            existing = res.scalars().first()

            login_url = f"{acct['whm_host']}"
            notes_str = f"cPanel User: {acct['cpanel_user']}\nEmail: {acct['email']}\nPlan: {acct['plan']}\nStart Date: {acct['start_date']}\nDisk Used: {acct['disk_used']} / {acct['disk_limit']}"
            enc_notes = encrypt_string(notes_str)
            status_str = "suspended" if acct.get("is_suspended") else "active"

            if existing:
                existing.provider = "cPanel / WHM"
                existing.ip_address = acct.get("ip_address")
                existing.server_hostname = acct.get("whm_host")
                existing.login_url = login_url
                existing.status = status_str
                existing.notes_encrypted = enc_notes
                imported_services.append(format_hosting_response(existing))
            else:
                new_service = HostingService(
                    name=domain_name,
                    provider="cPanel / WHM",
                    ip_address=acct.get("ip_address"),
                    server_hostname=acct.get("whm_host"),
                    login_url=login_url,
                    billing_cycle="yearly",
                    auto_renew=True,
                    status=status_str,
                    notes_encrypted=enc_notes,
                )
                db.add(new_service)
                await db.flush()
                imported_services.append(format_hosting_response(new_service))

        await db.commit()

    return {
        "status": "success",
        "total_fetched": len(accts),
        "total_imported": len(imported_services),
        "accounts": accts if not payload.import_to_db else imported_services,
    }


# ---------------------------------------------------------------------------
# General Endpoints
# ---------------------------------------------------------------------------

@router.get("", response_model=List[HostingResponse])
async def list_hosting(
    query: Optional[str] = Query(None),
    provider: Optional[str] = Query(None),
    status: Optional[str] = Query(None),
    db: AsyncSession = Depends(get_db),
):
    """List hosting services with filtering."""
    stmt = select(HostingService).order_by(HostingService.updated_at.desc())

    if query:
        q = f"%{query.strip()}%"
        stmt = stmt.where(
            (HostingService.name.ilike(q))
            | (HostingService.ip_address.ilike(q))
            | (HostingService.server_hostname.ilike(q))
            | (HostingService.provider.ilike(q))
        )
    if provider:
        stmt = stmt.where(HostingService.provider.ilike(f"%{provider.strip()}%"))

    result = await db.execute(stmt)
    services = result.scalars().all()

    formatted = [format_hosting_response(s) for s in services]

    if status:
        formatted = [s for s in formatted if s.status == status]

    return formatted


@router.get("/expiring", response_model=List[HostingResponse])
async def list_hosting_expiring_soon(
    days: int = Query(30, ge=1, le=365),
    db: AsyncSession = Depends(get_db),
):
    """Get hosting services expiring in specified days."""
    stmt = select(HostingService)
    result = await db.execute(stmt)
    services = result.scalars().all()

    formatted = [format_hosting_response(s) for s in services]
    return [s for s in formatted if s.status == "expiring_soon" or (s.days_until_expiry is not None and s.days_until_expiry <= days)]


@router.get("/{hosting_id}", response_model=HostingResponse)
async def get_hosting(hosting_id: int, db: AsyncSession = Depends(get_db)):
    """Get single hosting service."""
    service = await db.get(HostingService, hosting_id)
    if not service:
        raise HTTPException(status_code=404, detail="Hosting service not found")
    return format_hosting_response(service)


@router.post("", response_model=HostingResponse, status_code=status.HTTP_201_CREATED)
async def create_hosting(payload: HostingCreate, db: AsyncSession = Depends(get_db)):
    """Create hosting service manually."""
    enc_notes = encrypt_string(payload.notes) if payload.notes else None

    service = HostingService(
        name=payload.name.strip(),
        provider=payload.provider.strip(),
        ip_address=payload.ip_address.strip() if payload.ip_address else None,
        server_hostname=payload.server_hostname.strip() if payload.server_hostname else None,
        login_url=payload.login_url.strip() if payload.login_url else None,
        expiry_date=payload.expiry_date,
        billing_cycle=payload.billing_cycle,
        cost=payload.cost,
        currency=payload.currency,
        auto_renew=payload.auto_renew,
        status="active",
        notes_encrypted=enc_notes,
    )

    db.add(service)
    await db.commit()
    await db.refresh(service)

    return format_hosting_response(service)


@router.put("/{hosting_id}", response_model=HostingResponse)
async def update_hosting(hosting_id: int, payload: HostingUpdate, db: AsyncSession = Depends(get_db)):
    """Update hosting service."""
    service = await db.get(HostingService, hosting_id)
    if not service:
        raise HTTPException(status_code=404, detail="Hosting service not found")

    if payload.name is not None:
        service.name = payload.name.strip()
    if payload.provider is not None:
        service.provider = payload.provider.strip()
    if payload.ip_address is not None:
        service.ip_address = payload.ip_address.strip() if payload.ip_address.strip() else None
    if payload.server_hostname is not None:
        service.server_hostname = payload.server_hostname.strip() if payload.server_hostname.strip() else None
    if payload.login_url is not None:
        service.login_url = payload.login_url.strip() if payload.login_url.strip() else None
    if payload.expiry_date is not None:
        service.expiry_date = payload.expiry_date
    if payload.billing_cycle is not None:
        service.billing_cycle = payload.billing_cycle
    if payload.cost is not None:
        service.cost = payload.cost
    if payload.currency is not None:
        service.currency = payload.currency
    if payload.auto_renew is not None:
        service.auto_renew = payload.auto_renew
    if payload.status is not None:
        service.status = payload.status
    if payload.notes is not None:
        service.notes_encrypted = encrypt_string(payload.notes) if payload.notes.strip() else None

    await db.commit()
    await db.refresh(service)

    return format_hosting_response(service)


@router.delete("/{hosting_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_hosting(hosting_id: int, db: AsyncSession = Depends(get_db)):
    """Delete hosting service."""
    service = await db.get(HostingService, hosting_id)
    if not service:
        raise HTTPException(status_code=404, detail="Hosting service not found")

    await db.delete(service)
    await db.commit()
    return None
