"""Company service — now operates on Contact with type='company'.

Backward-compat layer: all company operations are now contact operations
filtered by type='company'. CompanyContact is replaced by ContactPerson 1:N.
"""

from __future__ import annotations

import uuid
from typing import Any

from sqlalchemy import select, func, delete
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.models.contact import Contact, ContactPerson


def _company_to_dict(c: Contact) -> dict:
    """Serialize a company (Contact with type=company) to dict."""
    return {
        "id": str(c.id),
        "name": c.name or "",
        "account_number": c.code or "",
        "industry": (c.custom or {}).get("industry") if c.custom else None,
        "phone": c.phone_1,
        "email": c.email_1,
        "website": c.website,
        "description": (c.custom or {}).get("description") if c.custom else None,
        "created_at": c.created_at.isoformat() if c.created_at else None,
        "updated_at": c.updated_at.isoformat() if c.updated_at else None,
    }


async def list_companies(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    page: int = 1,
    page_size: int = 20,
    search: str | None = None,
    industry: str | None = None,
    sort_by: str = "name",
    sort_order: str = "asc",
    resolved_perms: dict | None = None,
) -> dict:
    """List companies (contacts with type='company')."""
    base = select(Contact).where(
        Contact.tenant_id == tenant_id,
        Contact.type == "company",
        Contact.deleted_at.is_(None),
    )

    if search:
        base = base.where(Contact.search_tsv.op("@@")(func.plainto_tsquery("german", search)))

    count_q = select(func.count()).select_from(base.subquery())
    total = (await db.execute(count_q)).scalar() or 0

    sort_col = getattr(Contact, sort_by if sort_by != "name" else "name", Contact.name)
    if sort_order == "desc":
        sort_col = sort_col.desc()
    base = base.order_by(sort_col)

    offset = (page - 1) * page_size
    base = base.offset(offset).limit(page_size)

    result = await db.execute(base)
    companies = result.scalars().all()

    return {
        "items": [_company_to_dict(c) for c in companies],
        "total": total,
        "page": page,
        "page_size": page_size,
    }


async def create_company(
    db: AsyncSession, tenant_id: uuid.UUID, user_id: uuid.UUID, data: dict
) -> dict:
    """Create a company (contact with type='company')."""
    # Map old fields to new
    contact_data = {
        "type": "company",
        "name": data.get("name", ""),
        "code": data.get("account_number"),
        "phone_1": data.get("phone"),
        "email_1": data.get("email"),
        "website": data.get("website"),
        "displayname": data.get("name", ""),
    }
    # Store industry/description in custom
    custom = {}
    if data.get("industry"):
        custom["industry"] = data["industry"]
    if data.get("description"):
        custom["description"] = data["description"]
    if custom:
        contact_data["custom"] = custom

    contact = Contact(tenant_id=tenant_id, created_by=user_id, updated_by=user_id, **contact_data)
    db.add(contact)
    await db.flush()
    return _company_to_dict(contact)


async def get_company_detail(
    db: AsyncSession, tenant_id: uuid.UUID, company_id: str, resolved_perms: dict | None = None
) -> dict:
    """Get company detail with contact persons."""
    q = (
        select(Contact)
        .options(selectinload(Contact.contact_persons))
        .where(
            Contact.id == uuid.UUID(company_id),
            Contact.tenant_id == tenant_id,
            Contact.type == "company",
            Contact.deleted_at.is_(None),
        )
    )
    result = await db.execute(q)
    contact = result.scalar_one_or_none()
    if not contact:
        raise ValueError("Company not found")

    data = _company_to_dict(contact)
    data["contacts"] = [
        {
            "id": str(cp.id),
            "first_name": cp.firstname or "",
            "last_name": cp.lastname or "",
            "email": cp.email,
            "phone": cp.phone,
            "position": cp.function,
        }
        for cp in (contact.contact_persons or [])
        if cp.deleted_at is None
    ]
    return data


async def update_company(
    db: AsyncSession, tenant_id: uuid.UUID, user_id: uuid.UUID, company_id: str, data: dict
) -> dict:
    """Update a company."""
    q = select(Contact).where(
        Contact.id == uuid.UUID(company_id),
        Contact.tenant_id == tenant_id,
        Contact.type == "company",
        Contact.deleted_at.is_(None),
    )
    result = await db.execute(q)
    contact = result.scalar_one_or_none()
    if not contact:
        raise ValueError("Company not found")

    if "name" in data:
        contact.name = data["name"]
        contact.displayname = data["name"]
    if "account_number" in data:
        contact.code = data["account_number"]
    if "phone" in data:
        contact.phone_1 = data["phone"]
    if "email" in data:
        contact.email_1 = data["email"]
    if "website" in data:
        contact.website = data["website"]

    # Store industry/description in custom
    custom = dict(contact.custom or {})
    if "industry" in data:
        custom["industry"] = data["industry"]
    if "description" in data:
        custom["description"] = data["description"]
    if custom:
        contact.custom = custom

    contact.updated_by = user_id
    await db.flush()
    return _company_to_dict(contact)


async def soft_delete_company(
    db: AsyncSession, tenant_id: uuid.UUID, company_id: str, cascade: bool = True
) -> bool:
    """Soft-delete a company."""
    from datetime import datetime, timezone
    q = select(Contact).where(
        Contact.id == uuid.UUID(company_id),
        Contact.tenant_id == tenant_id,
        Contact.type == "company",
        Contact.deleted_at.is_(None),
    )
    result = await db.execute(q)
    contact = result.scalar_one_or_none()
    if not contact:
        return False
    contact.deleted_at = datetime.now(timezone.utc)
    await db.flush()
    return True


async def export_companies_xlsx(
    db: AsyncSession, tenant_id: uuid.UUID, industry: str | None = None, search: str | None = None
) -> bytes:
    """Export companies as XLSX."""
    from openpyxl import Workbook

    base = select(Contact).where(
        Contact.tenant_id == tenant_id,
        Contact.type == "company",
        Contact.deleted_at.is_(None),
    )
    if search:
        base = base.where(Contact.search_tsv.op("@@")(func.plainto_tsquery("german", search)))
    base = base.order_by(Contact.name)

    result = await db.execute(base)
    companies = result.scalars().all()

    wb = Workbook()
    ws = wb.active
    ws.title = "Companies"
    ws.append(["Name", "Account Number", "Industry", "Phone", "Email", "Website"])
    for c in companies:
        custom = c.custom or {}
        ws.append([
            c.name or "", c.code or "", custom.get("industry", ""),
            c.phone_1 or "", c.email_1 or "", c.website or "",
        ])

    import io
    buf = io.BytesIO()
    wb.save(buf)
    return buf.getvalue()


async def link_contact(
    db: AsyncSession, tenant_id: uuid.UUID, user_id: uuid.UUID, company_id: str, contact_id: str
) -> dict:
    """Link a contact person to a company (create ContactPerson)."""
    # In new model, contact_id is used to create a ContactPerson under the company contact
    # This is a compat shim — the old API expected a separate Contact entity
    # Now we just create a ContactPerson linked to the company
    cp = ContactPerson(
        tenant_id=tenant_id,
        contact_id=uuid.UUID(company_id),
        created_by=user_id,
        updated_by=user_id,
    )
    db.add(cp)
    await db.flush()
    return {"id": str(cp.id), "company_id": company_id, "contact_id": contact_id}


async def unlink_contact(
    db: AsyncSession, tenant_id: uuid.UUID, user_id: uuid.UUID, company_id: str, contact_id: str
) -> bool:
    """Unlink a contact person from a company (soft-delete ContactPerson)."""
    from datetime import datetime, timezone
    q = select(ContactPerson).where(
        ContactPerson.contact_id == uuid.UUID(company_id),
        ContactPerson.id == uuid.UUID(contact_id),
        ContactPerson.tenant_id == tenant_id,
        ContactPerson.deleted_at.is_(None),
    )
    result = await db.execute(q)
    cp = result.scalar_one_or_none()
    if not cp:
        return False
    cp.deleted_at = datetime.now(timezone.utc)
    await db.flush()
    return True
