"""Import/export service — CSV import with dry-run preview, CSV/XLSX export.

Uses unified Contact model fields: firstname, surname, email_1, phone_1, mobilephone, function.
Company import creates Contact with type='company'.
"""

from __future__ import annotations

import csv
import io
import uuid
from typing import Any

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.audit import log_audit
from app.models.contact import Contact
from app.services.contact_service import _serialize_contact as _contact_to_dict

# Expected CSV columns for each entity type
# Company import creates Contact with type='company' using name field
COMPANY_COLUMNS = ["name", "industry", "phone", "email", "website", "description"]
# Contact import uses unified Contact fields
CONTACT_COLUMNS = ["firstname", "surname", "email", "phone", "mobile", "function", "department"]


def _parse_csv(content: str) -> list[dict[str, str]]:
    """Parse CSV content into list of dicts."""
    reader = csv.DictReader(io.StringIO(content))
    return [dict(row) for row in reader]


def _validate_row(row: dict[str, str], required: list[str]) -> list[str]:
    """Validate a single row. Returns list of error messages (empty if valid)."""
    errors = []
    for col in required:
        val = row.get(col, "").strip()
        if not val:
            errors.append(f"Missing required field: {col}")
    return errors


async def import_companies(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    csv_content: str,
    dry_run: bool = False,
) -> dict[str, Any]:
    """Import companies from CSV as Contact with type='company'.

    Uses unified Contact model: name field for company name, email_1/phone_1 for contact info.
    """
    rows = _parse_csv(csv_content)
    total = len(rows)
    valid_rows = []
    errors = []

    for idx, row in enumerate(rows, start=1):
        row_errors = _validate_row(row, ["name"])
        if row_errors:
            for e in row_errors:
                errors.append({"row": idx, "error": e})
        else:
            valid_rows.append(row)

    if dry_run:
        return {
            "total": total,
            "valid": len(valid_rows),
            "invalid": len(errors),
            "errors": errors,
            "created": [],
            "dry_run": True,
        }

    created = []
    for row in valid_rows:
        contact = Contact(
            tenant_id=tenant_id,
            type="company",
            name=row["name"].strip(),
            displayname=row["name"].strip(),
            email_1=row.get("email", "").strip() or None,
            phone_1=row.get("phone", "").strip() or None,
            website=row.get("website", "").strip() or None,
            description=row.get("description", "").strip() or None,
            created_by=user_id,
            updated_by=user_id,
        )
        db.add(contact)
        await db.flush()
        await log_audit(
            db,
            tenant_id,
            user_id,
            "import",
            "contact",
            contact.id,
            changes={"name": contact.name, "type": "company"},
        )
        created.append(_contact_to_dict(contact))

    return {
        "total": total,
        "valid": len(valid_rows),
        "invalid": len(errors),
        "errors": errors,
        "created": created,
        "dry_run": False,
    }


async def import_contacts(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    csv_content: str,
    dry_run: bool = False,
) -> dict[str, Any]:
    """Import contacts from CSV using unified Contact model fields.

    CSV columns: firstname, surname, email, phone, mobile, function, department.
    Maps to Contact fields: firstname, surname, email_1, phone_1, mobilephone, function.
    """
    rows = _parse_csv(csv_content)
    total = len(rows)
    valid_rows = []
    errors = []

    for idx, row in enumerate(rows, start=1):
        # Accept both old (first_name/last_name) and new (firstname/surname) column names
        firstname = (row.get("firstname") or row.get("first_name") or "").strip()
        surname = (row.get("surname") or row.get("last_name") or "").strip()
        if not firstname and not surname:
            errors.append({"row": idx, "error": "Missing required field: firstname or surname"})
        else:
            # Normalize row to use unified field names
            row["firstname"] = firstname
            row["surname"] = surname
            valid_rows.append(row)

    if dry_run:
        return {
            "total": total,
            "valid": len(valid_rows),
            "invalid": len(errors),
            "errors": errors,
            "created": [],
            "dry_run": True,
        }

    created = []
    for row in valid_rows:
        contact = Contact(
            tenant_id=tenant_id,
            type="person",
            firstname=row["firstname"].strip() or None,
            surname=row["surname"].strip() or None,
            displayname=f"{row['firstname']} {row['surname']}",
            email_1=row.get("email", "").strip() or None,
            phone_1=row.get("phone", "").strip() or None,
            mobilephone=row.get("mobile", "").strip() or None,
            function=row.get("function", "").strip() or None,
            created_by=user_id,
            updated_by=user_id,
        )
        db.add(contact)
        await db.flush()
        await log_audit(
            db,
            tenant_id,
            user_id,
            "import",
            "contact",
            contact.id,
            changes={"firstname": contact.firstname, "surname": contact.surname},
        )
        created.append(_contact_to_dict(contact))

    return {
        "total": total,
        "valid": len(valid_rows),
        "invalid": len(errors),
        "errors": errors,
        "created": created,
        "dry_run": False,
    }


async def import_csv(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    csv_content: str,
    entity_type: str,
    dry_run: bool = False,
) -> dict[str, Any]:
    """Generic CSV import dispatcher based on entity_type ('companies' or 'contacts')."""
    if entity_type == "companies":
        return await import_companies(db, tenant_id, user_id, csv_content, dry_run=dry_run)
    elif entity_type == "contacts":
        return await import_contacts(db, tenant_id, user_id, csv_content, dry_run=dry_run)
    else:
        return {
            "total": 0,
            "valid": 0,
            "invalid": 0,
            "errors": [{"row": 0, "error": f"Unknown entity_type: {entity_type}"}],
            "created": [],
            "dry_run": dry_run,
        }


async def export_contacts_csv(
    db: AsyncSession,
    tenant_id: uuid.UUID,
) -> str:
    """Export contacts as CSV string using unified Contact model fields."""
    q = (
        select(Contact)
        .where(
            Contact.tenant_id == tenant_id,
            Contact.deleted_at.is_(None),
        )
        .order_by(Contact.surname, Contact.firstname)
    )
    result = await db.execute(q)
    contacts = result.scalars().all()

    output = io.StringIO()
    writer = csv.writer(output)
    writer.writerow(
        ["id", "type", "firstname", "surname", "name", "email", "phone", "mobile", "function", "city", "postalcode", "country"]
    )
    for c in contacts:
        writer.writerow(
            [
                str(c.id),
                c.type or "person",
                c.firstname or "",
                c.surname or "",
                c.name or "",
                c.email_1 or "",
                c.phone_1 or "",
                c.mobilephone or "",
                c.function or "",
                c.mailing_city or "",
                c.mailing_postalcode or "",
                c.mailing_country or "",
            ]
        )
    return output.getvalue()
