import logging
from typing import Optional, Any
from fastapi import APIRouter, HTTPException, Query, Request, Form, Depends
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from pathlib import Path
from datetime import date
from app.core.database import execute_query
from app.core.auth_dependencies import get_optional_user

logger = logging.getLogger(__name__)
router = APIRouter()

# Setup template directory - must be root "app" to allow extending shared/frontend/base.html
templates = Jinja2Templates(directory="app")


def build_location_tree(items: list) -> list:
    """Helper to build recursive location tree"""
    nodes = {}
    roots = []

    for loc in items or []:
        if not isinstance(loc, dict):
            continue
        loc_id = loc.get("id")
        if loc_id is None:
            continue
        # Ensure we have the fields we need
        nodes[loc_id] = {
            "id": loc_id,
            "name": loc.get("name"),
            "location_type": loc.get("location_type"),
            "parent_location_id": loc.get("parent_location_id"),
            "children": [] 
        }

    for node in nodes.values():
        parent_id = node.get("parent_location_id")
        if parent_id and parent_id in nodes:
            nodes[parent_id]["children"].append(node)
        else:
            roots.append(node)

    def sort_nodes(node_list: list) -> None:
        node_list.sort(key=lambda n: (n.get("name") or "").lower())
        for n in node_list:
            if n.get("children"):
                sort_nodes(n["children"])

    sort_nodes(roots)
    return roots


def extract_eset_specs_summary(hardware: dict) -> dict:
    specs = hardware.get("hardware_specs") or {}
    if not isinstance(specs, dict):
        return {}

    if "device" in specs and isinstance(specs.get("device"), dict):
        specs = specs["device"]

    def format_bytes(raw_value: Optional[str]) -> Optional[str]:
        if not raw_value:
            return None
        try:
            value = int(raw_value)
        except (TypeError, ValueError):
            return None
        if value <= 0:
            return None
        gb = value / (1024 ** 3)
        return f"{gb:.1f} GB"

    os_info = specs.get("operatingSystem") or {}
    os_name = os_info.get("displayName")
    os_version = (os_info.get("version") or {}).get("name")

    hardware_profiles = specs.get("hardwareProfiles") or []
    profile = hardware_profiles[0] if hardware_profiles else {}

    bios = profile.get("bios") or {}
    processors = profile.get("processors") or []
    hard_drives = profile.get("hardDrives") or []
    network_adapters = profile.get("networkAdapters") or []

    cpu_models = [p.get("caption") for p in processors if isinstance(p, dict) and p.get("caption")]
    disk_models = [d.get("displayName") for d in hard_drives if isinstance(d, dict) and d.get("displayName")]
    disk_sizes = [format_bytes(d.get("capacityBytes")) for d in hard_drives if isinstance(d, dict)]
    disk_summaries = []
    for drive in hard_drives:
        if not isinstance(drive, dict):
            continue
        name = drive.get("displayName")
        size = format_bytes(drive.get("capacityBytes"))
        if name and size:
            disk_summaries.append(f"{name} ({size})")
        elif name:
            disk_summaries.append(name)

    adapter_names = [n.get("caption") for n in network_adapters if isinstance(n, dict) and n.get("caption")]
    macs = [n.get("macAddress") for n in network_adapters if isinstance(n, dict) and n.get("macAddress")]

    installed_software = []

    def _normalize_version(value: Any) -> str:
        if isinstance(value, dict):
            name = str(value.get("name") or "").strip()
            if name:
                return name
            version_id = str(value.get("id") or "").strip()
            if version_id:
                return version_id
            major = value.get("major")
            minor = value.get("minor")
            patch = value.get("patch")
            if major is not None and minor is not None and patch is not None:
                return f"{major}.{minor}.{patch}"
            return ""
        if value is None:
            return ""
        return str(value).strip()

    def _add_software_item(name: Optional[str], version: Any = None) -> None:
        if not name:
            return
        item_name = str(name).strip()
        item_version = _normalize_version(version)
        if not item_name:
            return
        if item_version:
            installed_software.append(f"{item_name} {item_version}")
        else:
            installed_software.append(item_name)

    # ESET standard format
    for comp in specs.get("deployedComponents") or []:
        if not isinstance(comp, dict):
            continue
        _add_software_item(
            comp.get("displayName") or comp.get("name"),
            comp.get("version"),
        )

    # Alternative common payload names
    for comp in specs.get("installedSoftware") or []:
        if isinstance(comp, dict):
            _add_software_item(comp.get("displayName") or comp.get("name") or comp.get("softwareName"), comp.get("version"))
        elif isinstance(comp, str):
            _add_software_item(comp)

    for comp in specs.get("applications") or []:
        if isinstance(comp, dict):
            _add_software_item(comp.get("displayName") or comp.get("name") or comp.get("applicationName"), comp.get("version"))
        elif isinstance(comp, str):
            _add_software_item(comp)

    for comp in specs.get("activeProducts") or []:
        if isinstance(comp, dict):
            _add_software_item(
                comp.get("displayName") or comp.get("name") or comp.get("productName") or comp.get("product"),
                comp.get("version") or comp.get("productVersion"),
            )
        elif isinstance(comp, str):
            _add_software_item(comp)

    for key in ("applicationInventory", "softwareInventory"):
        for comp in specs.get(key) or []:
            if isinstance(comp, dict):
                _add_software_item(
                    comp.get("displayName") or comp.get("name") or comp.get("applicationName") or comp.get("softwareName"),
                    comp.get("version") or comp.get("applicationVersion") or comp.get("softwareVersion"),
                )
            elif isinstance(comp, str):
                _add_software_item(comp)

    # Keep ordering but remove duplicates
    seen = set()
    deduped_installed_software = []
    for item in installed_software:
        key = item.lower()
        if key in seen:
            continue
        seen.add(key)
        deduped_installed_software.append(item)

    installed_software_details = []
    for item in deduped_installed_software:
        match = item.rsplit(" ", 1)
        if len(match) == 2 and any(ch.isdigit() for ch in match[1]):
            installed_software_details.append({"name": match[0], "version": match[1]})
        else:
            installed_software_details.append({"name": item, "version": ""})

    return {
        "os_name": os_name,
        "os_version": os_version,
        "primary_local_ip": specs.get("primaryLocalIpAddress"),
        "public_ip": specs.get("publicIpAddress"),
        "device_name": specs.get("displayName"),
        "manufacturer": profile.get("manufacturer"),
        "model": profile.get("model"),
        "bios_serial": bios.get("serialNumber"),
        "bios_vendor": bios.get("manufacturer"),
        "cpu_models": cpu_models,
        "disk_models": disk_models,
        "disk_summaries": disk_summaries,
        "disk_sizes": disk_sizes,
        "adapter_names": adapter_names,
        "macs": macs,
        "functionality_status": specs.get("functionalityStatus"),
        "last_sync_time": specs.get("lastSyncTime"),
        "device_type": specs.get("deviceType"),
        "deployed_components": deduped_installed_software,
        "installed_software_details": installed_software_details,
    }


@router.get("/hardware", response_class=HTMLResponse)
async def hardware_list(
    request: Request,
    status: str = Query(None),
    asset_type: str = Query(None),
    rental_scope: str = Query(None),
    customer_id: int = Query(None),
    q: str = Query(None)
):
    """Display list of BMC-owned assets only."""
    query = """
        SELECT
            ha.*,
            c.name AS customer_name,
            CASE
                WHEN EXISTS (
                    SELECT 1
                    FROM subscription_asset_bindings b
                    WHERE b.asset_id = ha.id
                      AND b.deleted_at IS NULL
                      AND b.status = 'active'
                      AND (b.end_date IS NULL OR b.end_date >= CURRENT_DATE)
                ) THEN true
                ELSE false
            END AS is_currently_rented
        FROM hardware_assets ha
        LEFT JOIN customers c ON c.id = ha.current_owner_customer_id
                WHERE ha.deleted_at IS NULL
                    AND ha.current_owner_type = 'bmc'
    """
    params = []

    if status:
        query += " AND ha.status = %s"
        params.append(status)
    if asset_type:
        query += " AND ha.asset_type = %s"
        params.append(asset_type)
    if customer_id:
        query += " AND ha.current_owner_customer_id = %s"
        params.append(customer_id)
    if q:
        query += " AND (ha.serial_number ILIKE %s OR ha.model ILIKE %s OR ha.brand ILIKE %s OR ha.internal_asset_id ILIKE %s)"
        search_param = f"%{q}%"
        params.extend([search_param, search_param, search_param, search_param])

    if rental_scope == "rented":
        query += """
            AND EXISTS (
                SELECT 1
                FROM subscription_asset_bindings b
                WHERE b.asset_id = ha.id
                  AND b.deleted_at IS NULL
                  AND b.status = 'active'
                  AND (b.end_date IS NULL OR b.end_date >= CURRENT_DATE)
            )
        """
    elif rental_scope == "not_rented":
        query += """
            AND NOT EXISTS (
                SELECT 1
                FROM subscription_asset_bindings b
                WHERE b.asset_id = ha.id
                  AND b.deleted_at IS NULL
                  AND b.status = 'active'
                  AND (b.end_date IS NULL OR b.end_date >= CURRENT_DATE)
            )
        """

    query += " ORDER BY ha.created_at DESC"
    hardware = execute_query(query, tuple(params))

    return templates.TemplateResponse("modules/hardware/templates/index.html", {
        "request": request,
        "hardware": hardware,
        "current_status": status,
        "current_asset_type": asset_type,
        "current_rental_scope": rental_scope,
        "search_query": q
    })


@router.get("/hardware/customers", response_class=HTMLResponse)
async def customer_hardware_list(
    request: Request,
    status: str = Query(None),
    asset_type: str = Query(None),
    customer_id: int = Query(None),
    q: str = Query(None)
):
    """Display customer-owned hardware on dedicated page."""
    query = """
        SELECT
            ha.*,
            c.name AS customer_name
        FROM hardware_assets ha
        LEFT JOIN customers c ON c.id = ha.current_owner_customer_id
        WHERE ha.deleted_at IS NULL
          AND ha.current_owner_type = 'customer'
    """
    params = []

    if status:
        query += " AND ha.status = %s"
        params.append(status)
    if asset_type:
        query += " AND ha.asset_type = %s"
        params.append(asset_type)
    if customer_id:
        query += " AND ha.current_owner_customer_id = %s"
        params.append(customer_id)
    if q:
        query += " AND (ha.serial_number ILIKE %s OR ha.model ILIKE %s OR ha.brand ILIKE %s OR ha.internal_asset_id ILIKE %s OR c.name ILIKE %s)"
        search_param = f"%{q}%"
        params.extend([search_param, search_param, search_param, search_param, search_param])

    query += " ORDER BY c.name ASC NULLS LAST, ha.created_at DESC"
    customer_hardware = execute_query(query, tuple(params))

    return templates.TemplateResponse("modules/hardware/templates/customers.html", {
        "request": request,
        "hardware": customer_hardware,
        "current_status": status,
        "current_asset_type": asset_type,
        "search_query": q,
        "current_customer_id": customer_id,
    })


@router.get("/hardware/new", response_class=HTMLResponse)
async def create_hardware_form(request: Request):
    """Display create hardware form."""
    # Get customers for dropdown
    customers = execute_query("SELECT id, name AS navn FROM customers WHERE deleted_at IS NULL ORDER BY name")
    
    return templates.TemplateResponse("modules/hardware/templates/create.html", {
        "request": request,
        "customers": customers or []
    })


@router.get("/hardware/eset", response_class=HTMLResponse)
async def hardware_eset_overview(
    request: Request,
    current_user: dict = Depends(get_optional_user),
):
    """Display ESET sync overview (matches + incidents)."""
    matches_query = """
        SELECT
            h.id,
            h.asset_type,
            h.brand,
            h.model,
            h.serial_number,
            h.eset_uuid,
            h.eset_group,
            h.updated_at,
            hc.contact_id,
            c.first_name,
            c.last_name,
            c.user_company,
            cc.customer_id,
            cust.name AS customer_name
        FROM hardware_assets h
        LEFT JOIN hardware_contacts hc ON hc.hardware_id = h.id
        LEFT JOIN contacts c ON c.id = hc.contact_id
        LEFT JOIN contact_companies cc ON cc.contact_id = c.id
        LEFT JOIN customers cust ON cust.id = cc.customer_id
        WHERE h.deleted_at IS NULL
        ORDER BY h.updated_at DESC NULLS LAST
        LIMIT 500
    """
    matches = execute_query(matches_query)

    incidents_query = """
        SELECT
            i.*,
            hw.id AS hardware_id,
            hw.current_owner_customer_id AS customer_id,
            cust.name AS customer_name
        FROM eset_incidents i
        LEFT JOIN LATERAL (
            SELECT h.id, h.current_owner_customer_id
            FROM hardware_assets h
            WHERE h.deleted_at IS NULL
              AND LOWER(COALESCE(h.eset_uuid, '')) = LOWER(COALESCE(i.device_uuid, ''))
            ORDER BY h.updated_at DESC NULLS LAST, h.id DESC
            LIMIT 1
        ) hw ON TRUE
        LEFT JOIN customers cust ON cust.id = hw.current_owner_customer_id
        WHERE LOWER(COALESCE(i.severity, '')) IN ('critical', 'high', 'severe')
        ORDER BY i.updated_at DESC NULLS LAST
        LIMIT 5
    """
    incidents = execute_query(incidents_query)

    return templates.TemplateResponse("modules/hardware/templates/eset_overview.html", {
        "request": request,
        "matches": matches or [],
        "incidents": incidents or [],
        "current_user": current_user,
    })


@router.get("/hardware/eset/test", response_class=HTMLResponse)
async def hardware_eset_test(request: Request):
    """Display ESET API test page."""
    return templates.TemplateResponse("modules/hardware/templates/eset_test.html", {
        "request": request
    })


@router.get("/hardware/eset/import", response_class=HTMLResponse)
async def hardware_eset_import(request: Request):
    """Display ESET import page."""
    return templates.TemplateResponse("modules/hardware/templates/eset_import.html", {
        "request": request
    })


@router.get("/hardware/{hardware_id:int}", response_class=HTMLResponse)
async def hardware_detail(request: Request, hardware_id: int):
    """Display hardware details."""
    # Get hardware
    query = "SELECT * FROM hardware_assets WHERE id = %s AND deleted_at IS NULL"
    result = execute_query(query, (hardware_id,))
    if not result:
        raise HTTPException(status_code=404, detail="Hardware not found")
    
    hardware = result[0]
    
    # Get customer name if applicable
    if hardware.get('current_owner_customer_id'):
        customer_query = "SELECT name AS navn FROM customers WHERE id = %s"
        customer_result = execute_query(customer_query, (hardware['current_owner_customer_id'],))
        if customer_result:
            hardware['customer_name'] = customer_result[0]['navn']
    
    # Get ownership history
    ownership_query = """
        SELECT * FROM hardware_ownership_history 
        WHERE hardware_id = %s AND deleted_at IS NULL
        ORDER BY start_date DESC
    """
    ownership = execute_query(ownership_query, (hardware_id,))
    
    # Get customer names for ownership history
    if ownership:
        customer_ids = [o['owner_customer_id'] for o in ownership if o.get('owner_customer_id')]
        if customer_ids:
            customer_query = "SELECT id, name AS navn FROM customers WHERE id = ANY(%s)"
            customers = execute_query(customer_query, (customer_ids,))
            customer_map = {c['id']: c['navn'] for c in customers} if customers else {}
            
            for o in ownership:
                if o.get('owner_customer_id'):
                    o['customer_name'] = customer_map.get(o['owner_customer_id'], 'Unknown')
    
    # Get location history
    location_query = """
        SELECT * FROM hardware_location_history 
        WHERE hardware_id = %s AND deleted_at IS NULL
        ORDER BY start_date DESC
    """
    locations = execute_query(location_query, (hardware_id,))
    
    # Get attachments
    attachment_query = """
        SELECT * FROM hardware_attachments 
        WHERE hardware_id = %s AND deleted_at IS NULL
        ORDER BY uploaded_at DESC
    """
    attachments = execute_query(attachment_query, (hardware_id,))
    
    # Get related cases
    case_query = """
        SELECT hcr.*, s.titel, s.status, s.customer_id
        FROM hardware_case_relations hcr
        LEFT JOIN sag_sager s ON hcr.case_id = s.id
        WHERE hcr.hardware_id = %s AND hcr.deleted_at IS NULL AND s.deleted_at IS NULL
        ORDER BY hcr.created_at DESC
    """
    cases = execute_query(case_query, (hardware_id,))
    
    # Get tags
    tag_query = """
        SELECT * FROM hardware_tags 
        WHERE hardware_id = %s AND deleted_at IS NULL
        ORDER BY created_at DESC
    """
    tags = execute_query(tag_query, (hardware_id,))

    # Get linked contacts
    contacts_query = """
        SELECT hc.*, c.first_name, c.last_name, c.email, c.phone, c.mobile
        FROM hardware_contacts hc
        JOIN contacts c ON c.id = hc.contact_id
        WHERE hc.hardware_id = %s
        ORDER BY hc.role = 'primary' DESC, c.first_name ASC
    """
    contacts = execute_query(contacts_query, (hardware_id,))

    # Get available contacts for linking (from current owner)
    available_contacts = []
    if hardware.get('current_owner_customer_id'):
        avail_query = """
            SELECT c.id, c.first_name, c.last_name, c.email
            FROM contacts c
            JOIN contact_companies cc ON c.id = cc.contact_id
            WHERE cc.customer_id = %s 
            AND c.is_active = TRUE
            AND c.id NOT IN (
                SELECT contact_id FROM hardware_contacts WHERE hardware_id = %s
            )
            ORDER BY cc.is_primary DESC, c.first_name ASC
        """
        available_contacts = execute_query(avail_query, (hardware['current_owner_customer_id'], hardware_id))

    # Get customers for ownership selector
    owner_customers_query = """
        SELECT id, name AS navn
        FROM customers
        ORDER BY name
    """
    owner_customers = execute_query(owner_customers_query)

    owner_contacts_query = """
        SELECT
            cc.customer_id,
            c.id,
            c.first_name,
            c.last_name,
            c.email,
            c.phone,
            cc.is_primary
        FROM contacts c
        JOIN contact_companies cc ON cc.contact_id = c.id
        WHERE c.is_active = TRUE
        ORDER BY cc.customer_id, cc.is_primary DESC, c.first_name ASC, c.last_name ASC
    """
    owner_contacts = execute_query(owner_contacts_query)
    
    # Get all active locations for the tree (including parent_id for structure)
    all_locations_query = """
        SELECT id, name, location_type, parent_location_id 
        FROM locations_locations 
        WHERE deleted_at IS NULL 
        ORDER BY name
    """
    all_locations_flat = execute_query(all_locations_query)
    location_tree = build_location_tree(all_locations_flat)

    # Rental statistics for this hardware asset
    rental_overview_query = """
        SELECT
            COUNT(*) AS total_bindings,
            COALESCE(SUM(CASE WHEN b.status = 'active' AND b.deleted_at IS NULL THEN 1 ELSE 0 END), 0) AS active_bindings,
            MIN(b.start_date) AS first_rented_at,
            MAX(COALESCE(b.end_date, b.start_date)) AS latest_rental_activity,
            COALESCE(SUM(
                CASE
                    WHEN b.status = 'active'
                     AND b.deleted_at IS NULL
                     AND s.status IN ('active', 'paused')
                    THEN COALESCE(i.line_total, 0)
                    ELSE 0
                END
            ), 0) AS active_mrr,
            COALESCE(SUM(
                CASE
                    WHEN b.status = 'active'
                     AND b.deleted_at IS NULL
                     AND s.status IN ('draft', 'active', 'paused')
                    THEN COALESCE(i.line_total, 0)
                    ELSE 0
                END
            ), 0) AS pipeline_mrr
        FROM subscription_asset_bindings b
        LEFT JOIN sag_subscriptions s ON s.id = b.subscription_id
        LEFT JOIN sag_subscription_items i
               ON i.subscription_id = s.id
              AND i.asset_id = b.asset_id
        WHERE b.asset_id = %s
    """
    rental_overview = execute_query(rental_overview_query, (hardware_id,))
    rental_overview = (rental_overview or [{}])[0]

    rental_revenue_query = """
        SELECT
            COALESCE(SUM(
                CASE
                    WHEN o.sync_status IN ('exported', 'posted', 'paid')
                    THEN ((line->>'quantity')::numeric * (line->>'unit_price')::numeric)
                    ELSE 0
                END
            ), 0) AS exported_or_posted_revenue,
            COALESCE(SUM(
                CASE
                    WHEN o.sync_status = 'pending'
                    THEN ((line->>'quantity')::numeric * (line->>'unit_price')::numeric)
                    ELSE 0
                END
            ), 0) AS pending_revenue,
            COALESCE(SUM((line->>'quantity')::numeric * (line->>'unit_price')::numeric), 0) AS total_revenue,
            COUNT(DISTINCT o.id) AS order_count,
            COUNT(DISTINCT CASE WHEN o.sync_status IN ('exported', 'posted', 'paid') THEN o.id END) AS exported_or_posted_order_count,
            COUNT(DISTINCT CASE WHEN o.sync_status = 'pending' THEN o.id END) AS pending_order_count
        FROM ordre_drafts o
        CROSS JOIN LATERAL jsonb_array_elements(COALESCE(o.lines_json, '[]'::jsonb)) line
        WHERE line ? 'source_id'
          AND (line->>'source_id') ~ '^[0-9]+$'
          AND (line->>'source_id')::integer = %s
    """
    rental_revenue = execute_query(rental_revenue_query, (hardware_id,))
    rental_revenue = (rental_revenue or [{}])[0]

    recent_rentals_query = """
        SELECT
            o.id,
            o.title,
            o.sync_status,
            o.created_at,
            COALESCE(SUM((line->>'quantity')::numeric * (line->>'unit_price')::numeric), 0) AS amount
        FROM ordre_drafts o
        CROSS JOIN LATERAL jsonb_array_elements(COALESCE(o.lines_json, '[]'::jsonb)) line
        WHERE line ? 'source_id'
          AND (line->>'source_id') ~ '^[0-9]+$'
          AND (line->>'source_id')::integer = %s
        GROUP BY o.id, o.title, o.sync_status, o.created_at
        ORDER BY o.created_at DESC
        LIMIT 8
    """
    recent_rentals = execute_query(recent_rentals_query, (hardware_id,))

    rental_stats = {
        "total_bindings": int(rental_overview.get("total_bindings") or 0),
        "active_bindings": int(rental_overview.get("active_bindings") or 0),
        "first_rented_at": rental_overview.get("first_rented_at"),
        "latest_rental_activity": rental_overview.get("latest_rental_activity"),
        "active_mrr": float(rental_overview.get("active_mrr") or 0),
        "pipeline_mrr": float(rental_overview.get("pipeline_mrr") or 0),
        "exported_or_posted_revenue": float(rental_revenue.get("exported_or_posted_revenue") or 0),
        "pending_revenue": float(rental_revenue.get("pending_revenue") or 0),
        "total_revenue": float(rental_revenue.get("total_revenue") or 0),
        "order_count": int(rental_revenue.get("order_count") or 0),
        "exported_or_posted_order_count": int(rental_revenue.get("exported_or_posted_order_count") or 0),
        "pending_order_count": int(rental_revenue.get("pending_order_count") or 0),
    }
    
    return templates.TemplateResponse("modules/hardware/templates/detail.html", {
        "request": request,
        "hardware": hardware,
        "ownership": ownership or [],
        "locations": locations or [],
        "attachments": attachments or [],
        "cases": cases or [],
        "tags": tags or [],
        "contacts": contacts or [],
        "available_contacts": available_contacts or [],
        "owner_customers": owner_customers or [],
        "owner_contacts": owner_contacts or [],
        "location_tree": location_tree or [],
        "eset_specs": extract_eset_specs_summary(hardware),
        "rental_stats": rental_stats,
        "recent_rentals": recent_rentals or [],
    })


@router.get("/hardware/{hardware_id:int}/edit", response_class=HTMLResponse)
async def edit_hardware_form(request: Request, hardware_id: int):
    """Display edit hardware form."""
    # Get hardware
    query = "SELECT * FROM hardware_assets WHERE id = %s AND deleted_at IS NULL"
    result = execute_query(query, (hardware_id,))
    if not result:
        raise HTTPException(status_code=404, detail="Hardware not found")
    
    hardware = result[0]
    
    # Get customers for dropdown
    customers = execute_query("SELECT id, name AS navn FROM customers WHERE deleted_at IS NULL ORDER BY name")
    
    return templates.TemplateResponse("modules/hardware/templates/edit.html", {
        "request": request,
        "hardware": hardware,
        "customers": customers or []
    })


@router.post("/hardware/{hardware_id:int}/location")
async def update_hardware_location(
    request: Request,
    hardware_id: int,
    location_id: int = Form(...),
    notes: str = Form(None)
):
    """Update hardware location."""
    # Verify hardware exists
    check_query = "SELECT id FROM hardware_assets WHERE id = %s AND deleted_at IS NULL"
    if not execute_query(check_query, (hardware_id,)):
        raise HTTPException(status_code=404, detail="Hardware not found")

    # Verify location exists
    loc_check = "SELECT name FROM locations_locations WHERE id = %s"
    loc_result = execute_query(loc_check, (location_id,))
    if not loc_result:
        raise HTTPException(status_code=404, detail="Location not found")
    location_name = loc_result[0]['name']

    # 1. Close current location history
    close_history_query = """
        UPDATE hardware_location_history
        SET end_date = %s
        WHERE hardware_id = %s AND end_date IS NULL
    """
    execute_query(close_history_query, (date.today(), hardware_id))

    # 2. Add new location history
    add_history_query = """
        INSERT INTO hardware_location_history (hardware_id, location_id, location_name, start_date, notes)
        VALUES (%s, %s, %s, %s, %s)
    """
    execute_query(add_history_query, (hardware_id, location_id, location_name, date.today(), notes))

    # 3. Update current location on asset
    update_asset_query = """
        UPDATE hardware_assets
        SET current_location_id = %s, updated_at = NOW()
        WHERE id = %s
    """
    execute_query(update_asset_query, (location_id, hardware_id))

    return RedirectResponse(url=f"/hardware/{hardware_id}", status_code=303)


@router.post("/hardware/{hardware_id:int}/owner")
async def update_hardware_owner(
    request: Request,
    hardware_id: int,
    owner_customer_id: int = Form(...),
    owner_contact_id: Optional[int] = Form(None),
    notes: Optional[str] = Form(None)
):
    """Update hardware ownership."""
    # Verify hardware exists
    check_query = "SELECT id FROM hardware_assets WHERE id = %s AND deleted_at IS NULL"
    if not execute_query(check_query, (hardware_id,)):
        raise HTTPException(status_code=404, detail="Hardware not found")

    # Verify customer exists
    customer_check = "SELECT id FROM customers WHERE id = %s AND deleted_at IS NULL"
    if not execute_query(customer_check, (owner_customer_id,)):
        raise HTTPException(status_code=404, detail="Customer not found")

    # Verify owner contact belongs to selected customer
    if owner_contact_id:
        contact_check_query = """
            SELECT 1
            FROM contact_companies
            WHERE customer_id = %s AND contact_id = %s
            LIMIT 1
        """
        if not execute_query(contact_check_query, (owner_customer_id, owner_contact_id)):
            raise HTTPException(status_code=400, detail="Selected contact does not belong to customer")

    # Close active ownership history
    close_history_query = """
        UPDATE hardware_ownership_history
        SET end_date = %s
        WHERE hardware_id = %s AND end_date IS NULL AND deleted_at IS NULL
    """
    execute_query(close_history_query, (date.today(), hardware_id))

    # Insert new ownership history
    add_history_query = """
        INSERT INTO hardware_ownership_history (
            hardware_id, owner_type, owner_customer_id, start_date, notes
        )
        VALUES (%s, %s, %s, %s, %s)
    """
    execute_query(add_history_query, (hardware_id, "customer", owner_customer_id, date.today(), notes))

    # Update current owner on hardware
    update_asset_query = """
        UPDATE hardware_assets
        SET current_owner_type = %s, current_owner_customer_id = %s, updated_at = NOW()
        WHERE id = %s
    """
    execute_query(update_asset_query, ("customer", owner_customer_id, hardware_id))

    # Optionally set owner contact as primary for this hardware
    if owner_contact_id:
        demote_query = """
            UPDATE hardware_contacts
            SET role = 'user'
            WHERE hardware_id = %s AND role = 'primary'
        """
        execute_query(demote_query, (hardware_id,))

        owner_contact_query = """
            INSERT INTO hardware_contacts (hardware_id, contact_id, role, source)
            VALUES (%s, %s, 'primary', 'manual')
            ON CONFLICT (hardware_id, contact_id)
            DO UPDATE SET role = EXCLUDED.role, source = EXCLUDED.source
        """
        execute_query(owner_contact_query, (hardware_id, owner_contact_id))

    return RedirectResponse(url=f"/hardware/{hardware_id}", status_code=303)


@router.post("/hardware/{hardware_id:int}/contacts/add")
async def add_hardware_contact(
    request: Request,
    hardware_id: int,
    contact_id: int = Form(...)
):
    """Link a contact to hardware."""
    # Check if exists
    exists_query = "SELECT id FROM hardware_contacts WHERE hardware_id = %s AND contact_id = %s"
    if execute_query(exists_query, (hardware_id, contact_id)):
         # Already exists, just redirect
         pass
    else:
        insert_query = """
            INSERT INTO hardware_contacts (hardware_id, contact_id, role, source)
            VALUES (%s, %s, 'user', 'manual')
        """
        execute_query(insert_query, (hardware_id, contact_id))
    
    return RedirectResponse(url=f"/hardware/{hardware_id}", status_code=303)


@router.post("/hardware/{hardware_id:int}/contacts/{contact_id:int}/delete")
async def remove_hardware_contact(
    request: Request,
    hardware_id: int,
    contact_id: int
):
    """Unlink a contact from hardware."""
    delete_query = "DELETE FROM hardware_contacts WHERE hardware_id = %s AND contact_id = %s"
    execute_query(delete_query, (hardware_id, contact_id))
    
    return RedirectResponse(url=f"/hardware/{hardware_id}", status_code=303)
