"""Unified Contact model — company or person, with inline addresses.

Based on Rentman's contact model: a single table with type field
('company' or 'person'). ContactPerson is a 1:N child for
ansprechpartner (company employees / contact persons).
"""

from __future__ import annotations

import uuid
from typing import Any

from sqlalchemy import (
    Computed,
 ForeignKey,
    Index,
    String,
    Text,
    Float,
    JSON,
)
from sqlalchemy.dialects.postgresql import TSVECTOR
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.core.db import Base, TenantMixin


class Contact(Base, TenantMixin):
    """Unified contact entity — can be a company or a person.

    type='company': name is the company name, firstname/surname empty.
    type='person': firstname/surname are the person's name, name empty.
    Both types can have contactpersons (1:N) and inline addresses
    (mailing, visit, invoice).
    """

    __tablename__ = "contacts"
    __table_args__ = (
        Index("ix_contacts_tenant_deleted", "tenant_id", "deleted_at"),
        Index("ix_contacts_tenant_type", "tenant_id", "type"),
        Index("ix_contacts_tenant_name", "tenant_id", "name"),
        Index("ix_contacts_tenant_displayname", "tenant_id", "displayname"),
        Index("ix_contacts_email", "email_1"),
        Index("ix_contacts_code", "code"),
        Index("ix_contacts_search_vec", "search_tsv", postgresql_using="gin"),
    )

    id: Mapped[uuid.UUID] = mapped_column(
        PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )

    # ── Identity & Type ──
    type: Mapped[str] = mapped_column(String(20), nullable=False, default="company")  # 'company' or 'person'
    displayname: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    name: Mapped[str | None] = mapped_column(String(255), nullable=True)  # company name
    firstname: Mapped[str | None] = mapped_column(String(100), nullable=True)
    surname: Mapped[str | None] = mapped_column(String(100), nullable=True)
    surfix: Mapped[str | None] = mapped_column(String(50), nullable=True)  # name prefix (Dr., Prof.)
    ext_name_line: Mapped[str | None] = mapped_column(String(255), nullable=True)  # additional name line / subtitle
    gender: Mapped[str | None] = mapped_column(String(20), nullable=True)

    # ── Customer / Accounting ──
    code: Mapped[str | None] = mapped_column(String(100), nullable=True)  # customer number
    accounting_code: Mapped[str | None] = mapped_column(String(100), nullable=True)
    vendor_accounting_code: Mapped[str | None] = mapped_column(String(100), nullable=True)

    # ── Mailing Address (inline) ──
    mailing_street: Mapped[str | None] = mapped_column(String(255), nullable=True)
    mailing_number: Mapped[str | None] = mapped_column(String(20), nullable=True)
    mailing_unit_number: Mapped[str | None] = mapped_column(String(50), nullable=True)
    mailing_district: Mapped[str | None] = mapped_column(String(100), nullable=True)
    mailing_extra_address_line: Mapped[str | None] = mapped_column(String(255), nullable=True)
    mailing_postalcode: Mapped[str | None] = mapped_column(String(20), nullable=True)
    mailing_city: Mapped[str | None] = mapped_column(String(100), nullable=True)
    mailing_state: Mapped[str | None] = mapped_column(String(100), nullable=True)
    mailing_country: Mapped[str | None] = mapped_column(String(2), nullable=True)

    # ── Visit Address (inline) ──
    visit_street: Mapped[str | None] = mapped_column(String(255), nullable=True)
    visit_number: Mapped[str | None] = mapped_column(String(20), nullable=True)
    visit_unit_number: Mapped[str | None] = mapped_column(String(50), nullable=True)
    visit_district: Mapped[str | None] = mapped_column(String(100), nullable=True)
    visit_extra_address_line: Mapped[str | None] = mapped_column(String(255), nullable=True)
    visit_postalcode: Mapped[str | None] = mapped_column(String(20), nullable=True)
    visit_city: Mapped[str | None] = mapped_column(String(100), nullable=True)
    visit_state: Mapped[str | None] = mapped_column(String(100), nullable=True)

    # ── Invoice Address (inline) ──
    invoice_street: Mapped[str | None] = mapped_column(String(255), nullable=True)
    invoice_number: Mapped[str | None] = mapped_column(String(20), nullable=True)
    invoice_unit_number: Mapped[str | None] = mapped_column(String(50), nullable=True)
    invoice_district: Mapped[str | None] = mapped_column(String(100), nullable=True)
    invoice_extra_address_line: Mapped[str | None] = mapped_column(String(255), nullable=True)
    invoice_postalcode: Mapped[str | None] = mapped_column(String(20), nullable=True)
    invoice_city: Mapped[str | None] = mapped_column(String(100), nullable=True)
    invoice_state: Mapped[str | None] = mapped_column(String(100), nullable=True)
    invoice_country: Mapped[str | None] = mapped_column(String(2), nullable=True)

    # ── General country ──
    country: Mapped[str | None] = mapped_column(String(2), nullable=True)

    # ── Communication ──
    phone_1: Mapped[str | None] = mapped_column(String(50), nullable=True)
    phone_2: Mapped[str | None] = mapped_column(String(50), nullable=True)
    email_1: Mapped[str | None] = mapped_column(String(255), nullable=True)
    email_2: Mapped[str | None] = mapped_column(String(255), nullable=True)
    website: Mapped[str | None] = mapped_column(String(500), nullable=True)

    # ── Financial & Tax ──
    vat_code: Mapped[str | None] = mapped_column(String(50), nullable=True)  # USt-IdNr.
    fiscal_code: Mapped[str | None] = mapped_column(String(50), nullable=True)  # Steuernummer
    commerce_code: Mapped[str | None] = mapped_column(String(100), nullable=True)  # Handelsregister
    purchase_number: Mapped[str | None] = mapped_column(String(100), nullable=True)  # Bestellnummer
    bic: Mapped[str | None] = mapped_column(String(50), nullable=True)
    bank_account: Mapped[str | None] = mapped_column(String(50), nullable=True)  # IBAN

    # ── Discounts ──
    discount_crew: Mapped[float] = mapped_column(Float, nullable=False, default=0)
    discount_transport: Mapped[float] = mapped_column(Float, nullable=False, default=0)
    discount_rental: Mapped[float] = mapped_column(Float, nullable=False, default=0)
    discount_sale: Mapped[float] = mapped_column(Float, nullable=False, default=0)
    discount_subrent: Mapped[float] = mapped_column(Float, nullable=False, default=0)
    discount_total: Mapped[float] = mapped_column(Float, nullable=False, default=0)

    # ── Geo ──
    latitude: Mapped[float | None] = mapped_column(Float, nullable=True)
    longitude: Mapped[float | None] = mapped_column(Float, nullable=True)

    # ── Notes & Warnings ──
    projectnote: Mapped[str | None] = mapped_column(Text, nullable=True)
    projectnote_title: Mapped[str | None] = mapped_column(String(255), nullable=True)
    contact_warning: Mapped[str | None] = mapped_column(Text, nullable=True)
    tags: Mapped[str | None] = mapped_column(String(500), nullable=True)  # comma-separated
    image: Mapped[str | None] = mapped_column(Text, nullable=True)  # logo/image URL or base64

    # ── Default contact persons (self-referential via contactpersons table) ──
    default_person_id: Mapped[uuid.UUID | None] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("contactpersons.id", ondelete="SET NULL"), nullable=True
    )
    admin_contactperson_id: Mapped[uuid.UUID | None] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("contactpersons.id", ondelete="SET NULL"), nullable=True
    )

    # ── Folder assignment ──
    folder_id: Mapped[uuid.UUID | None] = mapped_column(
        PGUUID(as_uuid=True),
        ForeignKey("contact_folders.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
    )

    # ── Custom fields ──
    custom: Mapped[dict | None] = mapped_column(JSON, nullable=True, default=dict)

    # ── FTS ──
    search_tsv: Mapped[Any] = mapped_column(
        TSVECTOR,
        Computed(
            "to_tsvector('german', coalesce(name, '') || ' ' || coalesce(displayname, '') || ' ' || coalesce(firstname, '') || ' ' || coalesce(surname, '') || ' ' || coalesce(email_1, '') || ' ' || coalesce(email_2, '') || ' ' || coalesce(code, '') || ' ' || coalesce(phone_1, '') || ' ' || coalesce(phone_2, '') || ' ' || coalesce(mailing_city, '') || ' ' || coalesce(mailing_postalcode, '') || ' ' || coalesce(tags, ''))",
            persisted=True,
        ),
        nullable=True,
    )

    # ── Audit ──
    created_by: Mapped[uuid.UUID | None] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True
    )
    updated_by: Mapped[uuid.UUID | None] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True
    )

    # ── Relationships ──
    contact_persons: Mapped[list[ContactPerson]] = relationship(
        back_populates="contact", cascade="all, delete-orphan", foreign_keys="ContactPerson.contact_id"
    )


class ContactPerson(Base, TenantMixin):
    """Ansprechpartner — 1:N child of a Contact.

    Represents a person working at / associated with a company contact.
    Has its own address and communication fields.
    """

    __tablename__ = "contactpersons"
    __table_args__ = (
        Index("ix_contactpersons_tenant_deleted", "tenant_id", "deleted_at"),
        Index("ix_contactpersons_contact", "contact_id"),
        Index("ix_contactpersons_email", "email"),
    )

    id: Mapped[uuid.UUID] = mapped_column(
        PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4
    )

    # ── Parent contact ──
    contact_id: Mapped[uuid.UUID] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("contacts.id", ondelete="CASCADE"), nullable=False
    )

    # ── Name ──
    displayname: Mapped[str] = mapped_column(String(255), nullable=False, default="")
    firstname: Mapped[str | None] = mapped_column(String(100), nullable=True)
    middle_name: Mapped[str | None] = mapped_column(String(100), nullable=True)
    lastname: Mapped[str | None] = mapped_column(String(100), nullable=True)
    function: Mapped[str | None] = mapped_column(String(255), nullable=True)  # position/role

    # ── Communication ──
    phone: Mapped[str | None] = mapped_column(String(50), nullable=True)
    mobilephone: Mapped[str | None] = mapped_column(String(50), nullable=True)
    email: Mapped[str | None] = mapped_column(String(255), nullable=True)

    # ── Own address ──
    street: Mapped[str | None] = mapped_column(String(255), nullable=True)
    number: Mapped[str | None] = mapped_column(String(20), nullable=True)
    postalcode: Mapped[str | None] = mapped_column(String(20), nullable=True)
    city: Mapped[str | None] = mapped_column(String(100), nullable=True)
    state: Mapped[str | None] = mapped_column(String(100), nullable=True)
    country: Mapped[str | None] = mapped_column(String(2), nullable=True)

    # ── Other ──
    tags: Mapped[str | None] = mapped_column(String(500), nullable=True)
    custom: Mapped[dict | None] = mapped_column(JSON, nullable=True, default=dict)

    # ── Audit ──
    created_by: Mapped[uuid.UUID | None] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True
    )
    updated_by: Mapped[uuid.UUID | None] = mapped_column(
        PGUUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True
    )

    # ── Relationship ──
    contact: Mapped[Contact] = relationship(
        back_populates="contact_persons", foreign_keys=[contact_id]
    )


# Keep old names for backward compat during migration
CompanyContact = None  # deprecated — replaced by ContactPerson 1:N
