"""
DevOps Ticket Bridge — FastAPI application.

Auto-generated API documentation lives at:
    Swagger UI:  http://localhost:8000/docs
    ReDoc:       http://localhost:8000/redoc
    OpenAPI:     http://localhost:8000/openapi.json
"""
from typing import List, Optional

import httpx
from dotenv import load_dotenv
from fastapi import Depends, FastAPI, HTTPException, status
from sqlalchemy.orm import Session

load_dotenv()  # must run before importing modules that read env vars

from database import Base, engine, get_db  # noqa: E402
import models  # noqa: E402
import schemas  # noqa: E402
import devops_client  # noqa: E402

# Create tables on startup (idempotent — won't recreate if they exist)
Base.metadata.create_all(bind=engine)

app = FastAPI(
    title="DevOps Ticket Bridge API",
    description=(
        "Receives customer support tickets and creates corresponding Azure DevOps "
        "work items, auto-assigned to the support email registered for the affected "
        "system.\n\n"
        "**Flow:** `POST /tickets` → validate product+system codes → look up support "
        "email by `system_code` → call Azure DevOps REST API → save linkage locally → "
        "return work item ID + URL.\n\n"
        "All work items are created under the single service account that owns the "
        "configured PAT. Routing/attribution is done via Azure DevOps `Tags` and the "
        "`AssignedTo` field."
    ),
    version="0.1.0",
)


# ============================================================
# PRODUCTS
# ============================================================
@app.post(
    "/products",
    response_model=schemas.ProductOut,
    status_code=status.HTTP_201_CREATED,
    tags=["Products"],
    summary="Create a product",
)
def create_product(payload: schemas.ProductCreate, db: Session = Depends(get_db)):
    if db.query(models.Product).filter_by(product_code=payload.product_code).first():
        raise HTTPException(409, f"Product code '{payload.product_code}' already exists")
    obj = models.Product(**payload.model_dump())
    db.add(obj)
    db.commit()
    db.refresh(obj)
    return obj


@app.get(
    "/products",
    response_model=List[schemas.ProductOut],
    tags=["Products"],
    summary="List all products",
)
def list_products(db: Session = Depends(get_db)):
    return db.query(models.Product).all()


@app.get(
    "/products/{product_code}",
    response_model=schemas.ProductOut,
    tags=["Products"],
    summary="Get a single product by code",
)
def get_product(product_code: str, db: Session = Depends(get_db)):
    obj = db.query(models.Product).filter_by(product_code=product_code).first()
    if not obj:
        raise HTTPException(404, "Product not found")
    return obj


@app.delete(
    "/products/{product_code}",
    tags=["Products"],
    summary="Delete a product (cascades to its systems and supports)",
)
def delete_product(product_code: str, db: Session = Depends(get_db)):
    obj = db.query(models.Product).filter_by(product_code=product_code).first()
    if not obj:
        raise HTTPException(404, "Product not found")
    db.delete(obj)
    db.commit()
    return {"deleted": product_code}


# ============================================================
# SYSTEMS
# ============================================================
@app.post(
    "/systems",
    response_model=schemas.SystemOut,
    status_code=status.HTTP_201_CREATED,
    tags=["Systems"],
    summary="Create a system under a product",
)
def create_system(payload: schemas.SystemCreate, db: Session = Depends(get_db)):
    if not db.query(models.Product).filter_by(product_code=payload.product_code).first():
        raise HTTPException(404, f"Product code '{payload.product_code}' not found")
    if db.query(models.System).filter_by(system_code=payload.system_code).first():
        raise HTTPException(409, f"System code '{payload.system_code}' already exists")
    obj = models.System(**payload.model_dump())
    db.add(obj)
    db.commit()
    db.refresh(obj)
    return obj


@app.get(
    "/systems",
    response_model=List[schemas.SystemOut],
    tags=["Systems"],
    summary="List systems (optionally filtered by product)",
)
def list_systems(
    product_code: Optional[str] = None, db: Session = Depends(get_db)
):
    q = db.query(models.System)
    if product_code:
        q = q.filter_by(product_code=product_code)
    return q.all()


@app.delete(
    "/systems/{system_code}",
    tags=["Systems"],
    summary="Delete a system (cascades to its support entries)",
)
def delete_system(system_code: str, db: Session = Depends(get_db)):
    obj = db.query(models.System).filter_by(system_code=system_code).first()
    if not obj:
        raise HTTPException(404, "System not found")
    db.delete(obj)
    db.commit()
    return {"deleted": system_code}


# ============================================================
# SUPPORT
# ============================================================
@app.post(
    "/supports",
    response_model=schemas.SupportOut,
    status_code=status.HTTP_201_CREATED,
    tags=["Support"],
    summary="Register a support email for a system",
)
def create_support(payload: schemas.SupportCreate, db: Session = Depends(get_db)):
    if not db.query(models.System).filter_by(system_code=payload.system_code).first():
        raise HTTPException(404, f"System code '{payload.system_code}' not found")
    obj = models.Support(**payload.model_dump())
    db.add(obj)
    db.commit()
    db.refresh(obj)
    return obj


@app.get(
    "/supports",
    response_model=List[schemas.SupportOut],
    tags=["Support"],
    summary="List support entries (optionally filtered by system)",
)
def list_supports(
    system_code: Optional[str] = None, db: Session = Depends(get_db)
):
    q = db.query(models.Support)
    if system_code:
        q = q.filter_by(system_code=system_code)
    return q.all()


@app.delete(
    "/supports/{support_id}",
    tags=["Support"],
    summary="Delete a support entry by id",
)
def delete_support(support_id: int, db: Session = Depends(get_db)):
    obj = db.query(models.Support).filter_by(id=support_id).first()
    if not obj:
        raise HTTPException(404, "Support entry not found")
    db.delete(obj)
    db.commit()
    return {"deleted": support_id}


# ============================================================
# TICKETS — the core flow
# ============================================================
@app.post(
    "/tickets",
    response_model=schemas.TicketOut,
    status_code=status.HTTP_201_CREATED,
    tags=["Tickets"],
    summary="Submit a ticket — creates the Azure DevOps work item",
    description=(
        "Steps:\n"
        "1. Validate product_code and system_code exist, and that the system "
        "belongs to the product.\n"
        "2. Look up the support email registered for the system.\n"
        "3. Create an Azure DevOps work item with that support email as AssignedTo.\n"
        "4. Save the ticket locally with the work item ID and URL.\n\n"
        "Returns the saved ticket including the Azure DevOps work item ID."
    ),
)
async def create_ticket(
    payload: schemas.TicketCreate, db: Session = Depends(get_db)
):
    # 1. Validate references
    system = db.query(models.System).filter_by(system_code=payload.system_code).first()
    if not system:
        raise HTTPException(404, f"System code '{payload.system_code}' not found")

    product = db.query(models.Product).filter_by(product_code=payload.product_code).first()
    if not product:
        raise HTTPException(404, f"Product code '{payload.product_code}' not found")

    if system.product_code != payload.product_code:
        raise HTTPException(
            400,
            f"System '{payload.system_code}' belongs to product "
            f"'{system.product_code}', not '{payload.product_code}'",
        )

    # 2. Resolve support email (first match — extend here for round-robin if needed)
    support = db.query(models.Support).filter_by(system_code=payload.system_code).first()
    assigned_email = support.support_email if support else None

    # 3. Build Azure DevOps payload
    title = (
        f"[{payload.product_code}/{payload.system_code}] "
        f"{payload.customer_company} — {payload.customer_mobilephone}"
    )
    description = (
        f"<b>Customer Company:</b> {payload.customer_company}<br>"
        f"<b>Customer Name:</b> {payload.customer_name}<br>"
        f"<b>Mobile:</b> {payload.customer_mobilephone}<br>"
        f"<b>Product:</b> {product.product_name} ({payload.product_code})<br>"
        f"<b>System:</b> {system.system_name} ({payload.system_code})<br>"
        f"<br><b>Issue:</b><br>{payload.issue}"
    )

    # 4. Call Azure DevOps
    try:
        result = await devops_client.create_work_item(
            title=title,
            description=description,
            assigned_to=assigned_email,
        )
    except httpx.HTTPStatusError as e:
        raise HTTPException(
            502,
            f"Azure DevOps API error {e.response.status_code}: {e.response.text[:500]}",
        )
    except Exception as e:
        raise HTTPException(502, f"Azure DevOps call failed: {e}")

    work_item_id = result.get("id")
    work_item_url = result.get("_links", {}).get("html", {}).get("href")

    # 5. Save locally
    ticket = models.Ticket(
        **payload.model_dump(),
        devops_work_item_id=work_item_id,
        devops_url=work_item_url,
        assigned_to=assigned_email,
    )
    db.add(ticket)
    db.commit()
    db.refresh(ticket)
    return ticket


@app.get(
    "/tickets",
    response_model=List[schemas.TicketOut],
    tags=["Tickets"],
    summary="List all tickets (newest first)",
)
def list_tickets(db: Session = Depends(get_db)):
    return db.query(models.Ticket).order_by(models.Ticket.id.desc()).all()


@app.get(
    "/tickets/{ticket_id}",
    response_model=schemas.TicketOut,
    tags=["Tickets"],
    summary="Get a single ticket by id",
)
def get_ticket(ticket_id: int, db: Session = Depends(get_db)):
    obj = db.query(models.Ticket).filter_by(id=ticket_id).first()
    if not obj:
        raise HTTPException(404, "Ticket not found")
    return obj


# ============================================================
# HEALTH
# ============================================================
@app.get("/health", tags=["Health"], summary="Service health check")
def health():
    return {"status": "ok"}


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)