"""Type definitions for Data Legion API responses.

These are TypedDict types for use with type checkers. All API responses
are returned as plain dicts — these types provide type hints only.
"""

from __future__ import annotations

import sys

if sys.version_info >= (3, 11):
    from typing import NotRequired, TypedDict
else:
    from typing import TypedDict

    from typing_extensions import NotRequired


# --- Nested types ---


class CleanedRaw(TypedDict, total=False):
    """Generic {cleaned, raw[]} structure used for titles, degrees, headlines, etc."""

    cleaned: str | None
    raw: list[str]


class Phone(TypedDict, total=False):
    """Phone number entry."""

    type: str | None
    number: str | None
    current: bool | None
    confidence: str | None
    last_seen: str | None
    num_sources: int | None


class Email(TypedDict, total=False):
    """Email entry."""

    address: str | None
    type: str | None
    current: bool | None
    validated: bool | None
    confidence: str | None
    last_seen: str | None
    num_sources: int | None
    validation_status: str | None
    hash_sha256: str | None
    hash_sha1: str | None
    hash_md5: str | None


class Location(TypedDict, total=False):
    """Location entry."""

    street_address: str | None
    address_line_2: str | None
    city: str | None
    state: str | None
    state_code: str | None
    country: str | None
    country_code: str | None
    continent: str | None
    continent_code: str | None
    postal_code: str | None
    geo: str | None
    raw: list[str]
    current: bool | None
    num_sources: int | None
    confidence: str | None
    last_seen: str | None


class ExperienceOrg(TypedDict, total=False):
    """Organization within an experience entry."""

    legion_id: str | None
    name: CleanedRaw | None
    website: str | None
    linkedin_url: str | None
    linkedin_id: str | None
    industry: str | None
    size: str | None


class Experience(TypedDict, total=False):
    """Experience entry."""

    title: CleanedRaw | None
    seniority_level: str | None
    job_function: str | None
    expense_category: str | None
    is_decision_maker: bool | None
    organization: ExperienceOrg | None
    start_date: str | None
    end_date: str | None
    current: bool | None
    tenure_months: int | None
    description: CleanedRaw | None


class EducationOrg(TypedDict, total=False):
    """Organization within an education entry."""

    name: CleanedRaw | None
    website: str | None
    linkedin_url: str | None


class Education(TypedDict, total=False):
    """Education entry."""

    organization: EducationOrg | None
    degree: CleanedRaw | None
    degree_level: str | None
    field_of_study: CleanedRaw | None
    start_date: str | None
    end_date: str | None
    current: bool | None


class Social(TypedDict, total=False):
    """Social profile entry."""

    network: str | None
    url: str | None
    username: str | None
    id: str | None
    current: bool | None
    num_sources: int | None
    confidence: str | None
    last_seen: str | None


class Skill(TypedDict, total=False):
    """Skill entry."""

    cleaned: str | None
    raw: list[str]


class Language(TypedDict, total=False):
    """Language entry."""

    cleaned: str | None
    raw: list[str]
    proficiency: str | None


class MatchMetadata(TypedDict, total=False):
    """Metadata about the match."""

    matched_on: list[str]
    match_type: str
    match_confidence: str


# --- Person response types ---


class PersonResponse(TypedDict, total=False):
    """Complete person enrichment response."""

    legion_id: str

    # Identity
    full_name: str | None
    first_name: str | None
    middle_name: str | None
    middle_initial: str | None
    last_name: str | None
    last_initial: str | None
    suffix: str | None
    prefix: str | None
    sex: str | None
    birth_date: str | None
    birth_year: int | None
    birth_month: int | None
    birth_day: int | None
    age: int | None

    # Contact info
    work_email: str | None
    mobile_phone: str | None
    linkedin_url: str | None
    linkedin_id: str | None

    # Location
    city: str | None
    state: str | None
    state_code: str | None
    country: str | None
    country_code: str | None

    # Professional
    job_title: str | None
    company_legion_id: str | None
    company_name: str | None
    company_domain: str | None
    company_linkedin_url: str | None
    company_linkedin_id: str | None
    company_industry: str | None
    company_size: str | None
    seniority_level: str | None
    job_function: str | None
    expense_category: str | None
    is_decision_maker: bool | None
    years_of_experience: int | None
    avg_tenure_months: float | None
    highest_degree_level: str | None

    # LinkedIn
    linkedin_followers: int | None
    linkedin_connections: int | None

    # Narrative
    headline: CleanedRaw | None
    summary: CleanedRaw | None

    # Metadata
    num_sources: int | None
    current_jobs_last_updated: str | None
    current_jobs_last_confirmed: str | None
    current_location_last_updated: str | None
    current_location_last_confirmed: str | None
    last_seen: str | None
    build_version: str | None

    # Related data
    phones: list[Phone]
    emails: list[Email]
    locations: list[Location]
    experience: list[Experience]
    education: list[Education]
    socials: list[Social]
    skills: list[Skill]
    languages: list[Language]


class PersonMatch(TypedDict):
    """Person response with optional match metadata."""

    person: PersonResponse
    match_metadata: NotRequired[MatchMetadata | None]


class PersonMatches(TypedDict):
    """Multiple person matches response."""

    matches: list[PersonMatch]
    total: int


# --- Company response types ---


class Ticker(TypedDict, total=False):
    """Ticker entry."""

    symbol: str | None
    exchange: str | None


class CompanySocial(TypedDict, total=False):
    """Company social profile entry."""

    network: str | None
    url: str | None
    username: str | None
    id: str | None


class CompanyDomain(TypedDict, total=False):
    """Company domain entry."""

    domain: str | None


class EmployeeCountByMonth(TypedDict, total=False):
    """Monthly employee count data point."""

    month: str | None
    count: int | None
    net_change: int | None
    growth_rate: float | None
    hires: int | None
    departures: int | None


class TimeBucketInt(TypedDict, total=False):
    """Time-bucketed integer metric (new hires, attrition)."""

    # Note: keys are "1m", "3m", "6m", "12m" in the JSON response.
    # TypedDict doesn't support keys starting with digits, so access via ["1m"] etc.
    pass


class TimeBucketFloat(TypedDict, total=False):
    """Time-bucketed float metric (growth rate, turnover rate)."""

    # Note: keys are "1m", "3m", "6m", "12m" in the JSON response.
    # TypedDict doesn't support keys starting with digits, so access via ["1m"] etc.
    pass


class CompanyResponse(TypedDict, total=False):
    """Complete company enrichment response."""

    legion_id: str

    # Base fields
    name: CleanedRaw | None
    headline: CleanedRaw | None
    description: CleanedRaw | None
    domain: str | None
    linkedin_url: str | None
    linkedin_id: str | None
    linkedin_followers: int | None
    linkedin_employee_count: int | None
    industry: str | None
    type: str | None
    size: str | None
    founded: int | None

    # Legion metrics (scalars)
    legion_employee_count: int | None
    legion_average_tenure: float | None

    # Legion metrics (time-bucketed)
    legion_new_hire_count: dict | None
    legion_attrition_count: dict | None
    legion_turnover_rate: dict | None
    legion_employee_growth_rate: dict | None

    # Distribution maps
    legion_seniority_distribution: dict | None
    legion_job_function_distribution: dict | None
    legion_expense_category_distribution: dict | None
    legion_tenure_distribution: dict | None
    legion_education_distribution: dict | None

    # Growth rate maps (each key maps to time buckets)
    legion_seniority_growth_rate: dict | None
    legion_job_function_growth_rate: dict | None
    legion_expense_category_growth_rate: dict | None

    # Array fields
    tickers: list[Ticker]
    socials: list[CompanySocial]
    domains: list[CompanyDomain]
    legion_employee_count_by_month: list[EmployeeCountByMonth]

    # Metadata
    num_sources: int | None
    last_seen: str | None
    build_version: str | None


class CompanyMatch(TypedDict):
    """Company response with optional match metadata."""

    company: CompanyResponse
    match_metadata: NotRequired[MatchMetadata | None]


class CompanyMatches(TypedDict):
    """Multiple company matches response."""

    matches: list[CompanyMatch]
    total: int


# --- Utility response types ---


class FieldCleanResult(TypedDict, total=False):
    """Result for a single cleaned field."""

    original: str
    cleaned: str | None
    normalized: bool


class FieldCleanResponse(TypedDict):
    """Response for batch field cleaning."""

    results: dict[str, FieldCleanResult]


class EmailHashResponse(TypedDict):
    """Response for email hashing."""

    email: str
    normalized_email: str
    hashes: dict[str, str]


class ValidationErrorDetail(TypedDict, total=False):
    """Validation error detail."""

    field: str
    value: str | None
    error: str
    suggestion: str | None


class ValidationWarningDetail(TypedDict, total=False):
    """Validation warning detail."""

    field: str
    value: str | None
    warning: str
    suggestion: str | None


class ValidationSuggestionDetail(TypedDict, total=False):
    """Validation suggestion detail."""

    field: str
    cleaned: str | None


class ValidationResponse(TypedDict):
    """Response for data validation."""

    valid: bool
    errors: list[ValidationErrorDetail]
    warnings: list[ValidationWarningDetail]
    suggestions: list[ValidationSuggestionDetail]


# --- Error response type ---


class ErrorResponse(TypedDict, total=False):
    """Error response from the API."""

    error: str
    message: str
    details: dict | list | None
