"""Company tests — ACs 1-13, 22-24: CRUD, search, filter, export, N:M, emails, audit, soft-delete."""

from __future__ import annotations

import pytest
from httpx import AsyncClient

from tests.conftest import ORIGIN_HEADER, login_client, seed_tenant_and_users


@pytest.mark.asyncio
class TestCompanyList:
    """ACs 1-3: List, search, filter+sort."""

    async def test_list_companies_returns_200_paginated(self, client: AsyncClient, db_session):
        """AC 1: GET /api/v1/companies -> 200 + paginated (total/page/page_size)."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        resp = await client.get("/api/v1/companies", headers=ORIGIN_HEADER)
        assert resp.status_code == 200
        data = resp.json()
        assert "items" in data
        assert "total" in data
        assert "page" in data
        assert "page_size" in data
        assert data["total"] == 1  # seed creates one company in tenant A

    async def test_list_companies_search_fts(self, client: AsyncClient, db_session):
        """AC 2: GET /api/v1/companies?search=Tech -> 200 + FTS results."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        # Create a company with 'Tech' in name/description
        resp = await client.post(
            "/api/v1/companies",
            json={"name": "TechCorp", "industry": "IT", "description": "Technology solutions"},
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 201
        # Search for 'Tech'
        resp = await client.get("/api/v1/companies?search=Tech", headers=ORIGIN_HEADER)
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] >= 1
        names = [item["name"] for item in data["items"]]
        assert "TechCorp" in names

    async def test_list_companies_filter_and_sort(self, client: AsyncClient, db_session):
        """AC 3: GET /api/v1/companies?industry=IT&sort_by=name&sort_order=asc -> 200 + filtered+sorted."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        # Create additional companies
        await client.post(
            "/api/v1/companies",
            json={"name": "ZetaTech", "industry": "IT"},
            headers=ORIGIN_HEADER,
        )
        await client.post(
            "/api/v1/companies",
            json={"name": "AlphaTech", "industry": "IT"},
            headers=ORIGIN_HEADER,
        )
        resp = await client.get(
            "/api/v1/companies?industry=IT&sort_by=name&sort_order=asc",
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 200
        data = resp.json()
        names = [item["name"] for item in data["items"]]
        assert names == sorted(names)
        assert all(item["industry"] == "IT" for item in data["items"])


@pytest.mark.asyncio
class TestCompanyCreate:
    """ACs 4-5: Create valid, missing name."""

    async def test_create_company_valid_returns_201(self, client: AsyncClient, db_session):
        """AC 4: POST /api/v1/companies valid -> 201 + company object."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        resp = await client.post(
            "/api/v1/companies",
            json={"name": "New Corp", "industry": "Finance"},
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 201
        data = resp.json()
        assert data["name"] == "New Corp"
        assert data["industry"] == "Finance"
        assert "id" in data

    async def test_create_company_missing_name_returns_422(self, client: AsyncClient, db_session):
        """AC 5: POST /api/v1/companies missing name -> 422."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        resp = await client.post(
            "/api/v1/companies",
            json={"industry": "Finance"},
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 422


@pytest.mark.asyncio
class TestCompanyDetail:
    """AC 6: Get single company with contacts array."""

    async def test_get_company_returns_200_with_contacts(self, client: AsyncClient, db_session):
        """AC 6: GET /api/v1/companies/{id} -> 200 + detail inkl. contacts array."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        # Create a company
        create_resp = await client.post(
            "/api/v1/companies",
            json={"name": "Detail Corp"},
            headers=ORIGIN_HEADER,
        )
        company_id = create_resp.json()["id"]
        resp = await client.get(f"/api/v1/companies/{company_id}", headers=ORIGIN_HEADER)
        assert resp.status_code == 200
        data = resp.json()
        assert data["name"] == "Detail Corp"
        assert "contacts" in data
        assert isinstance(data["contacts"], list)


@pytest.mark.asyncio
class TestCompanyUpdate:
    """AC 7: Update company."""

    async def test_update_company_returns_200(self, client: AsyncClient, db_session):
        """AC 7: PUT /api/v1/companies/{id} -> 200 + updated."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        create_resp = await client.post(
            "/api/v1/companies",
            json={"name": "Old Name"},
            headers=ORIGIN_HEADER,
        )
        company_id = create_resp.json()["id"]
        resp = await client.put(
            f"/api/v1/companies/{company_id}",
            json={"name": "New Name", "industry": "Healthcare"},
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 200
        data = resp.json()
        assert data["name"] == "New Name"
        assert data["industry"] == "Healthcare"


@pytest.mark.asyncio
class TestCompanyDelete:
    """ACs 8-9: Soft-delete, cascade delete."""

    async def test_delete_company_soft_delete_returns_204(self, client: AsyncClient, db_session):
        """AC 8: DELETE /api/v1/companies/{id} -> 204, deleted_at gesetzt (soft-delete)."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        create_resp = await client.post(
            "/api/v1/companies",
            json={"name": "Delete Me"},
            headers=ORIGIN_HEADER,
        )
        company_id = create_resp.json()["id"]
        resp = await client.delete(f"/api/v1/companies/{company_id}", headers=ORIGIN_HEADER)
        assert resp.status_code == 204
        # Verify company is not in list (soft-deleted)
        list_resp = await client.get("/api/v1/companies", headers=ORIGIN_HEADER)
        names = [item["name"] for item in list_resp.json()["items"]]
        assert "Delete Me" not in names

    async def test_delete_company_cascade_returns_204(self, client: AsyncClient, db_session):
        """AC 9: DELETE /api/v1/companies/{id}?cascade=true -> 204, company + links geloescht."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        # Create company and contact
        comp_resp = await client.post(
            "/api/v1/companies",
            json={"name": "Cascade Corp"},
            headers=ORIGIN_HEADER,
        )
        company_id = comp_resp.json()["id"]
        cont_resp = await client.post(
            "/api/v1/contacts",
            json={"first_name": "John", "last_name": "Doe", "company_ids": [company_id]},
            headers=ORIGIN_HEADER,
        )
        cont_resp.json()["id"]
        # Verify link exists
        detail_resp = await client.get(f"/api/v1/companies/{company_id}", headers=ORIGIN_HEADER)
        assert len(detail_resp.json()["contacts"]) == 1
        # Cascade delete
        resp = await client.delete(
            f"/api/v1/companies/{company_id}?cascade=true",
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 204


@pytest.mark.asyncio
class TestCompanyContactLink:
    """ACs 10-11: N:M link, unlink."""

    async def test_link_contact_to_company_returns_200(self, client: AsyncClient, db_session):
        """AC 10: POST /api/v1/companies/{id}/contacts/{cid} -> 200, N:M link."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        comp_resp = await client.post(
            "/api/v1/companies",
            json={"name": "Link Corp"},
            headers=ORIGIN_HEADER,
        )
        company_id = comp_resp.json()["id"]
        cont_resp = await client.post(
            "/api/v1/contacts",
            json={"first_name": "Jane", "last_name": "Smith"},
            headers=ORIGIN_HEADER,
        )
        contact_id = cont_resp.json()["id"]
        resp = await client.post(
            f"/api/v1/companies/{company_id}/contacts/{contact_id}",
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 200
        data = resp.json()
        assert data["company_id"] == company_id
        assert data["contact_id"] == contact_id

    async def test_unlink_contact_from_company_returns_204(self, client: AsyncClient, db_session):
        """AC 11: DELETE /api/v1/companies/{id}/contacts/{cid} -> 204, N:M unlink."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        comp_resp = await client.post(
            "/api/v1/companies",
            json={"name": "Unlink Corp"},
            headers=ORIGIN_HEADER,
        )
        company_id = comp_resp.json()["id"]
        cont_resp = await client.post(
            "/api/v1/contacts",
            json={"first_name": "Bob", "last_name": "Wilson"},
            headers=ORIGIN_HEADER,
        )
        contact_id = cont_resp.json()["id"]
        # Link first
        await client.post(
            f"/api/v1/companies/{company_id}/contacts/{contact_id}",
            headers=ORIGIN_HEADER,
        )
        # Unlink
        resp = await client.delete(
            f"/api/v1/companies/{company_id}/contacts/{contact_id}",
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 204
        # Verify contact is no longer linked
        detail = await client.get(f"/api/v1/companies/{company_id}", headers=ORIGIN_HEADER)
        assert len(detail.json()["contacts"]) == 0


@pytest.mark.asyncio
class TestCompanyExport:
    """ACs 12-13: CSV export, XLSX export."""

    async def test_export_csv_returns_200_text_csv(self, client: AsyncClient, db_session):
        """AC 12: GET /api/v1/companies/export?format=csv -> 200 + text/csv."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        resp = await client.get(
            "/api/v1/companies/export?format=csv",
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 200
        assert "text/csv" in resp.headers.get("content-type", "")
        assert "name" in resp.text  # CSV header

    async def test_export_xlsx_returns_200_openxml(self, client: AsyncClient, db_session):
        """AC 13: GET /api/v1/companies/export?format=xlsx -> 200 + openxmlformats."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        resp = await client.get(
            "/api/v1/companies/export?format=xlsx",
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 200
        ct = resp.headers.get("content-type", "")
        assert "spreadsheet" in ct or "openxml" in ct
        assert len(resp.content) > 0


@pytest.mark.asyncio
class TestCompanyEmails:
    """AC 22: Emails endpoint (mail plugin inactive)."""

    async def test_get_company_emails_returns_200_empty(self, client: AsyncClient, db_session):
        """AC 22: GET /api/v1/companies/{id}/emails -> 200 (empty array, mail plugin inactive)."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        comp_resp = await client.post(
            "/api/v1/companies",
            json={"name": "Email Corp"},
            headers=ORIGIN_HEADER,
        )
        company_id = comp_resp.json()["id"]
        resp = await client.get(
            f"/api/v1/companies/{company_id}/emails",
            headers=ORIGIN_HEADER,
        )
        assert resp.status_code == 200
        assert resp.json() == []


@pytest.mark.asyncio
class TestCompanyAuditAndSoftDelete:
    """ACs 23-24: Audit log, soft-delete filter."""

    async def test_audit_log_on_company_create(self, client: AsyncClient, db_session):
        """AC 23: Audit log entry on every company/contact mutation."""
        from sqlalchemy import select

        from app.models.audit import AuditLog

        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        await client.post(
            "/api/v1/companies",
            json={"name": "Audited Corp"},
            headers=ORIGIN_HEADER,
        )
        q = select(AuditLog).where(AuditLog.entity_type == "contact", AuditLog.action == "create")
        result = await db_session.execute(q)
        entries = result.scalars().all()
        assert len(entries) >= 1
        assert any(e.changes and e.changes.get("name") == "Audited Corp" for e in entries)

    async def test_soft_deleted_company_not_in_list(self, client: AsyncClient, db_session):
        """AC 24: Soft-deleted company not in GET list (deleted_at IS NULL filter)."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        # Create and then delete a company
        create_resp = await client.post(
            "/api/v1/companies",
            json={"name": "SoftDelete Corp"},
            headers=ORIGIN_HEADER,
        )
        company_id = create_resp.json()["id"]
        await client.delete(f"/api/v1/companies/{company_id}", headers=ORIGIN_HEADER)
        # List should not contain deleted company
        list_resp = await client.get("/api/v1/companies", headers=ORIGIN_HEADER)
        names = [item["name"] for item in list_resp.json()["items"]]
        assert "SoftDelete Corp" not in names
        assert "Company Alpha" in names  # Seed company still present
