"""
ESET sync jobs
"""
import logging
from typing import Any, Dict, List, Optional
from psycopg2.extras import Json

from app.core.config import settings
from app.core.database import execute_query
from app.services.eset_service import eset_service

logger = logging.getLogger(__name__)


def _extract_first_str(payload: Any, keys: List[str]) -> Optional[str]:
    if payload is None:
        return None

    key_set = {k.lower() for k in keys}
    stack = [payload]
    while stack:
        current = stack.pop()
        if isinstance(current, dict):
            for k, v in current.items():
                if k.lower() in key_set and isinstance(v, str) and v.strip():
                    return v.strip()
                if isinstance(v, (dict, list)):
                    stack.append(v)
        elif isinstance(current, list):
            for item in current:
                if isinstance(item, (dict, list)):
                    stack.append(item)
    return None


def _extract_devices(payload: Any) -> List[Dict[str, Any]]:
    if isinstance(payload, list):
        return [d for d in payload if isinstance(d, dict)]
    if isinstance(payload, dict):
        for key in ("devices", "items", "results", "data"):
            value = payload.get(key)
            if isinstance(value, list):
                return [d for d in value if isinstance(d, dict)]
    return []


def _extract_company(payload: Any) -> Optional[str]:
    company = _extract_first_str(payload, ["company", "organization", "tenant", "customer", "userCompany"])
    if company:
        return company

    group_path = _extract_group_path(payload)
    if group_path and "/" in group_path:
        return group_path.split("/")[-1].strip() or None
    return None


def _extract_group_path(payload: Any) -> Optional[str]:
    return _extract_first_str(payload, ["parentGroup", "groupPath", "group", "path"])


def _extract_group_name(payload: Any) -> Optional[str]:
    group_path = _extract_group_path(payload)
    if group_path and "/" in group_path:
        name = group_path.split("/")[-1].strip()
        return name or None
    return group_path


def _extract_full_name(payload: Any) -> Optional[str]:
    name = _extract_first_str(payload, ["realName", "displayName", "userName", "owner", "user", "lastLoggedInUser"])
    if name:
        return name

    first = _extract_first_str(payload, ["firstName", "givenName"])
    last = _extract_first_str(payload, ["lastName", "surname", "familyName"])
    if first and last:
        return f"{first} {last}".strip()
    return None


def _extract_login_candidates(payload: Any) -> List[str]:
    raw = _extract_first_str(
        payload,
        ["userPrincipalName", "upn", "email", "mail", "loginName", "login", "userName", "lastLoggedInUser"]
    )
    if not raw:
        return []

    candidates: List[str] = []

    def _add(value: str) -> None:
        v = (value or "").strip().lower()
        if v and v not in candidates:
            candidates.append(v)

    _add(raw)
    # DOMAIN\\user or provider/user -> user
    if "\\" in raw:
        _add(raw.split("\\")[-1])
    if "/" in raw:
        _add(raw.split("/")[-1])

    # email local-part fallback
    if "@" in raw:
        _add(raw.split("@", 1)[0])

    return candidates


def _detect_asset_type(payload: Any) -> str:
    device_type = _extract_first_str(payload, ["deviceType", "type"])
    if device_type:
        val = device_type.lower()
        if "server" in val:
            return "server"
        if "laptop" in val or "notebook" in val:
            return "laptop"
    return "pc"


def _match_contact(full_name: str, company: str) -> Optional[int]:
    query = """
        SELECT id
        FROM contacts
        WHERE LOWER(TRIM(first_name || ' ' || last_name)) = LOWER(%s)
          AND LOWER(COALESCE(user_company, '')) = LOWER(%s)
        LIMIT 1
    """
    result = execute_query(query, (full_name, company))
    if result:
        return result[0]["id"]
    return None


def _match_contact_by_login(login_candidate: str, company: Optional[str] = None) -> Optional[int]:
    if not login_candidate:
        return None

    # Try scoped match first when company is known to reduce false positives.
    if company:
        scoped_query = """
            SELECT id
            FROM contacts
            WHERE LOWER(COALESCE(email, '')) = LOWER(%s)
              AND LOWER(COALESCE(user_company, '')) = LOWER(%s)
            LIMIT 1
        """
        scoped = execute_query(scoped_query, (login_candidate, company))
        if scoped:
            return scoped[0]["id"]

        scoped_local_part_query = """
            SELECT id
            FROM contacts
            WHERE LOWER(split_part(COALESCE(email, ''), '@', 1)) = LOWER(%s)
              AND LOWER(COALESCE(user_company, '')) = LOWER(%s)
            LIMIT 1
        """
        scoped_local_part = execute_query(scoped_local_part_query, (login_candidate, company))
        if scoped_local_part:
            return scoped_local_part[0]["id"]

    email_query = """
        SELECT id
        FROM contacts
        WHERE LOWER(COALESCE(email, '')) = LOWER(%s)
        LIMIT 1
    """
    by_email = execute_query(email_query, (login_candidate,))
    if by_email:
        return by_email[0]["id"]

    local_part_query = """
        SELECT id
        FROM contacts
        WHERE LOWER(split_part(COALESCE(email, ''), '@', 1)) = LOWER(%s)
        LIMIT 1
    """
    by_local_part = execute_query(local_part_query, (login_candidate,))
    if by_local_part:
        return by_local_part[0]["id"]

    return None


def _get_contact_customer(contact_id: int) -> Optional[int]:
    query = """
        SELECT customer_id
        FROM contact_companies
        WHERE contact_id = %s
        ORDER BY is_primary DESC, id ASC
        LIMIT 1
    """
    result = execute_query(query, (contact_id,))
    if result:
        return result[0]["customer_id"]
    return None


def _match_customer_exact(name: str) -> Optional[int]:
    if not name:
        return None
    query = "SELECT id FROM customers WHERE LOWER(name) = LOWER(%s)"
    result = execute_query(query, (name,))
    if len(result or []) == 1:
        return result[0]["id"]
    return None


def _upsert_hardware_contact(hardware_id: int, contact_id: int) -> None:
    query = """
        INSERT INTO hardware_contacts (hardware_id, contact_id, role, source)
        VALUES (%s, %s, %s, %s)
        ON CONFLICT (hardware_id, contact_id) DO NOTHING
    """
    execute_query(query, (hardware_id, contact_id, "primary", "eset"))


def _upsert_incident(incident: Dict[str, Any]) -> None:
    incident_uuid = incident.get("incidentUuid") or incident.get("uuid") or incident.get("id")
    if not incident_uuid:
        return

    severity = incident.get("severity") or incident.get("level")
    status = incident.get("status")
    device_uuid = incident.get("deviceUuid") or incident.get("device")
    detected_at = incident.get("detectedAt") or incident.get("firstSeen")
    last_seen = incident.get("lastSeen") or incident.get("lastUpdate")

    query = """
        INSERT INTO eset_incidents (
            incident_uuid, severity, status, device_uuid, detected_at, last_seen, payload, updated_at
        )
        VALUES (%s, %s, %s, %s, %s, %s, %s, NOW())
        ON CONFLICT (incident_uuid) DO UPDATE SET
            severity = EXCLUDED.severity,
            status = EXCLUDED.status,
            device_uuid = EXCLUDED.device_uuid,
            detected_at = EXCLUDED.detected_at,
            last_seen = EXCLUDED.last_seen,
            payload = EXCLUDED.payload,
            updated_at = NOW()
    """
    execute_query(query, (
        incident_uuid,
        severity,
        status,
        device_uuid,
        detected_at,
        last_seen,
        Json(incident)
    ))


async def sync_eset_hardware() -> None:
    if not settings.ESET_ENABLED or not settings.ESET_SYNC_ENABLED:
        return

    payload = await eset_service.list_devices()
    if not payload:
        logger.warning("ESET device list empty")
        return

    devices = _extract_devices(payload)
    logger.info("ESET devices fetched: %d", len(devices))

    for device in devices:
        device_uuid = device.get("deviceUuid") or device.get("uuid") or device.get("id")
        if not device_uuid:
            continue

        details = await eset_service.get_device_details(device_uuid)
        if not details:
            continue

        serial = _extract_first_str(details, ["serialNumber", "serial", "serial_number"])
        model = _extract_first_str(details, ["model", "deviceModel", "deviceName", "name"])
        brand = _extract_first_str(details, ["manufacturer", "brand", "vendor"])
        group_path = _extract_group_path(details)
        group_name = _extract_group_name(details)

        conditions = []
        params = []
        conditions.append("eset_uuid = %s")
        params.append(device_uuid)
        if serial:
            conditions.append("serial_number = %s")
            params.append(serial)

        lookup_query = f"SELECT * FROM hardware_assets WHERE deleted_at IS NULL AND ({' OR '.join(conditions)})"
        existing = execute_query(lookup_query, tuple(params))

        full_name = _extract_full_name(details)
        company = _extract_company(details)
        login_candidates = _extract_login_candidates(details)

        contact_id = _match_contact(full_name, company) if full_name and company else None
        if not contact_id:
            for login_candidate in login_candidates:
                contact_id = _match_contact_by_login(login_candidate, company)
                if contact_id:
                    break
        customer_id = _get_contact_customer(contact_id) if contact_id else None
        if not customer_id:
            customer_id = _match_customer_exact(group_name or company) if (group_name or company) else None

        if existing:
            hardware_id = existing[0]["id"]
            update_fields = ["eset_uuid = %s", "hardware_specs = %s", "updated_at = NOW()"]
            update_params = [device_uuid, Json(details)]

            if group_path:
                update_fields.append("eset_group = %s")
                update_params.append(group_path)

            if not existing[0].get("serial_number") and serial:
                update_fields.append("serial_number = %s")
                update_params.append(serial)
            if not existing[0].get("model") and model:
                update_fields.append("model = %s")
                update_params.append(model)
            if not existing[0].get("brand") and brand:
                update_fields.append("brand = %s")
                update_params.append(brand)

            # Auto-created ESET devices are customer devices by default unless explicitly reassigned.
            if customer_id:
                update_fields.append("current_owner_type = %s")
                update_params.append("customer")
                update_fields.append("current_owner_customer_id = %s")
                update_params.append(customer_id)
            elif existing[0].get("notes") == "Auto-created from ESET" and existing[0].get("current_owner_type") != "customer":
                update_fields.append("current_owner_type = %s")
                update_params.append("customer")

            update_params.append(hardware_id)
            update_query = f"""
                UPDATE hardware_assets
                SET {', '.join(update_fields)}
                WHERE id = %s
            """
            execute_query(update_query, tuple(update_params))
        else:
            # ESET sync auto-creates customer endpoints; ownership can be refined later if needed.
            owner_type = "customer"
            insert_query = """
                INSERT INTO hardware_assets (
                    asset_type, brand, model, serial_number,
                    current_owner_type, current_owner_customer_id,
                    notes, eset_uuid, hardware_specs, eset_group
                )
                VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
                RETURNING id
            """
            insert_params = (
                _detect_asset_type(details),
                brand,
                model,
                serial,
                owner_type,
                customer_id,
                "Auto-created from ESET",
                device_uuid,
                Json(details),
                group_path
            )
            created = execute_query(insert_query, insert_params)
            hardware_id = created[0]["id"] if created else None

        if contact_id and hardware_id:
            _upsert_hardware_contact(hardware_id, contact_id)
            if customer_id:
                owner_query = """
                    UPDATE hardware_assets
                    SET current_owner_type = %s, current_owner_customer_id = %s, updated_at = NOW()
                    WHERE id = %s
                """
                execute_query(owner_query, ("customer", customer_id, hardware_id))


async def sync_eset_incidents() -> None:
    if not settings.ESET_ENABLED or not settings.ESET_INCIDENTS_ENABLED:
        return

    payload = await eset_service.list_incidents()
    if not payload:
        logger.warning("ESET incidents list empty")
        return

    incidents = _extract_devices(payload)
    critical = 0

    for incident in incidents:
        _upsert_incident(incident)
        severity = (incident.get("severity") or incident.get("level") or "").lower()
        if severity in {"critical", "high", "severe"}:
            critical += 1

    if critical:
        logger.warning("ESET critical incidents: %d", critical)


async def run_eset_sync() -> None:
    await sync_eset_hardware()
    await sync_eset_incidents()
