"""Common Pydantic models for Crustdata MCP Server."""

from __future__ import annotations

import sys
from typing import Any, Literal, Union

from pydantic import BaseModel, Field

# Python 3.10 compatibility: StrEnum was added in 3.11
if sys.version_info >= (3, 11):
    from enum import StrEnum
else:
    from enum import Enum

    class StrEnum(str, Enum):
        """String enumeration for Python 3.10 compatibility."""

        def __str__(self) -> str:
            return str(self.value)


# ============================================================================
# Filter Type Enums
# ============================================================================


class CompanySearchFilterType(StrEnum):
    """Valid filter types for real-time company search via LinkedIn Sales Navigator."""

    COMPANY_HEADCOUNT = "COMPANY_HEADCOUNT"
    REGION = "REGION"
    INDUSTRY = "INDUSTRY"
    NUM_OF_FOLLOWERS = "NUM_OF_FOLLOWERS"
    FORTUNE = "FORTUNE"
    ACCOUNT_ACTIVITIES = "ACCOUNT_ACTIVITIES"
    JOB_OPPORTUNITIES = "JOB_OPPORTUNITIES"
    COMPANY_HEADCOUNT_GROWTH = "COMPANY_HEADCOUNT_GROWTH"
    ANNUAL_REVENUE = "ANNUAL_REVENUE"
    DEPARTMENT_HEADCOUNT = "DEPARTMENT_HEADCOUNT"
    KEYWORD = "KEYWORD"


class PeopleSearchFilterType(StrEnum):
    """Valid filter types for real-time people search via LinkedIn Sales Navigator."""

    CURRENT_COMPANY = "CURRENT_COMPANY"
    PAST_COMPANY = "PAST_COMPANY"
    CURRENT_TITLE = "CURRENT_TITLE"
    PAST_TITLE = "PAST_TITLE"
    COMPANY_HEADQUARTERS = "COMPANY_HEADQUARTERS"
    COMPANY_HEADCOUNT = "COMPANY_HEADCOUNT"
    REGION = "REGION"
    INDUSTRY = "INDUSTRY"
    PROFILE_LANGUAGE = "PROFILE_LANGUAGE"
    SENIORITY_LEVEL = "SENIORITY_LEVEL"
    YEARS_AT_CURRENT_COMPANY = "YEARS_AT_CURRENT_COMPANY"
    YEARS_IN_CURRENT_POSITION = "YEARS_IN_CURRENT_POSITION"
    YEARS_OF_EXPERIENCE = "YEARS_OF_EXPERIENCE"
    FUNCTION = "FUNCTION"
    FIRST_NAME = "FIRST_NAME"
    LAST_NAME = "LAST_NAME"
    SCHOOL = "SCHOOL"
    RECENTLY_CHANGED_JOBS = "RECENTLY_CHANGED_JOBS"
    POSTED_ON_LINKEDIN = "POSTED_ON_LINKEDIN"
    IN_THE_NEWS = "IN_THE_NEWS"
    KEYWORD = "KEYWORD"


class LinkedinAutocompleteFilterType(StrEnum):
    """Valid filter types for LinkedIn autocomplete API."""

    REGION = "region"
    INDUSTRY = "industry"
    TITLE = "title"
    SCHOOL = "school"


# ============================================================================
# Value Range Enums
# ============================================================================


class CompanyHeadcountRange(StrEnum):
    """Valid company headcount ranges for company search filters."""

    RANGE_1_10 = "1-10"
    RANGE_11_50 = "11-50"
    RANGE_51_200 = "51-200"
    RANGE_201_500 = "201-500"
    RANGE_501_1000 = "501-1,000"
    RANGE_1001_5000 = "1,001-5,000"
    RANGE_5001_10000 = "5,001-10,000"
    RANGE_10001_PLUS = "10,001+"


class PeopleCompanyHeadcountRange(StrEnum):
    """Valid company headcount ranges for people search filters (includes self-employed)."""

    SELF_EMPLOYED = "Self-employed"
    RANGE_1_10 = "1-10"
    RANGE_11_50 = "11-50"
    RANGE_51_200 = "51-200"
    RANGE_201_500 = "201-500"
    RANGE_501_1000 = "501-1,000"
    RANGE_1001_5000 = "1,001-5,000"
    RANGE_5001_10000 = "5,001-10,000"
    RANGE_10001_PLUS = "10,001+"


class NumOfFollowersRange(StrEnum):
    """Valid follower count ranges for company filters."""

    RANGE_1_50 = "1-50"
    RANGE_51_100 = "51-100"
    RANGE_101_1000 = "101-1000"
    RANGE_1001_5000 = "1001-5000"
    RANGE_5001_PLUS = "5001+"


class FortuneRanking(StrEnum):
    """Valid Fortune ranking categories."""

    FORTUNE_50 = "Fortune 50"
    FORTUNE_51_100 = "Fortune 51-100"
    FORTUNE_101_250 = "Fortune 101-250"
    FORTUNE_251_500 = "Fortune 251-500"


class AccountActivity(StrEnum):
    """Valid account activity types for company filters."""

    SENIOR_LEADERSHIP_CHANGES = "Senior leadership changes in last 3 months"
    FUNDING_EVENTS = "Funding events in past 12 months"


class JobOpportunity(StrEnum):
    """Valid job opportunity types."""

    HIRING_ON_LINKEDIN = "Hiring on Linkedin"


class SeniorityLevel(StrEnum):
    """Valid seniority levels for people search."""

    OWNER_PARTNER = "Owner / Partner"
    CXO = "CXO"
    VICE_PRESIDENT = "Vice President"
    DIRECTOR = "Director"
    EXPERIENCED_MANAGER = "Experienced Manager"
    ENTRY_LEVEL_MANAGER = "Entry Level Manager"
    STRATEGIC = "Strategic"
    SENIOR = "Senior"
    ENTRY_LEVEL = "Entry Level"
    IN_TRAINING = "In Training"


class ProfileLanguage(StrEnum):
    """Valid LinkedIn profile languages."""

    ARABIC = "Arabic"
    ENGLISH = "English"
    SPANISH = "Spanish"
    PORTUGUESE = "Portuguese"
    CHINESE = "Chinese"
    FRENCH = "French"
    ITALIAN = "Italian"
    RUSSIAN = "Russian"
    GERMAN = "German"
    DUTCH = "Dutch"
    TURKISH = "Turkish"
    TAGALOG = "Tagalog"
    POLISH = "Polish"
    KOREAN = "Korean"
    JAPANESE = "Japanese"
    MALAY = "Malay"
    NORWEGIAN = "Norwegian"
    DANISH = "Danish"
    ROMANIAN = "Romanian"
    SWEDISH = "Swedish"
    BAHASA_INDONESIA = "Bahasa Indonesia"
    CZECH = "Czech"


class YearsRange(StrEnum):
    """Valid years ranges for experience filters."""

    LESS_THAN_1 = "Less than 1 year"
    ONE_TO_TWO = "1 to 2 years"
    THREE_TO_FIVE = "3 to 5 years"
    SIX_TO_TEN = "6 to 10 years"
    MORE_THAN_10 = "More than 10 years"


class FunctionType(StrEnum):
    """Valid function/department types for people search."""

    ACCOUNTING = "Accounting"
    ADMINISTRATIVE = "Administrative"
    ARTS_AND_DESIGN = "Arts and Design"
    BUSINESS_DEVELOPMENT = "Business Development"
    COMMUNITY_AND_SOCIAL_SERVICES = "Community and Social Services"
    CONSULTING = "Consulting"
    EDUCATION = "Education"
    ENGINEERING = "Engineering"
    ENTREPRENEURSHIP = "Entrepreneurship"
    FINANCE = "Finance"
    HEALTHCARE_SERVICES = "Healthcare Services"
    HUMAN_RESOURCES = "Human Resources"
    INFORMATION_TECHNOLOGY = "Information Technology"
    LEGAL = "Legal"
    MARKETING = "Marketing"
    MEDIA_AND_COMMUNICATION = "Media and Communication"
    MILITARY_AND_PROTECTIVE_SERVICES = "Military and Protective Services"
    OPERATIONS = "Operations"
    PRODUCT_MANAGEMENT = "Product Management"
    PROGRAM_AND_PROJECT_MANAGEMENT = "Program and Project Management"
    PURCHASING = "Purchasing"
    QUALITY_ASSURANCE = "Quality Assurance"
    REAL_ESTATE = "Real Estate"
    RESEARCH = "Research"
    SALES = "Sales"
    CUSTOMER_SUCCESS_AND_SUPPORT = "Customer Success and Support"


class Currency(StrEnum):
    """Valid currencies for annual revenue filters."""

    USD = "USD"
    EUR = "EUR"
    GBP = "GBP"
    JPY = "JPY"
    CAD = "CAD"
    AUD = "AUD"
    CHF = "CHF"
    INR = "INR"
    CNY = "CNY"


class Department(StrEnum):
    """Valid departments for company headcount filters."""

    SALES = "Sales"
    ENGINEERING = "Engineering"
    OPERATIONS = "Operations"
    FINANCE = "Finance"
    HR = "HR"
    MARKETING = "Marketing"
    IT = "IT"
    R_AND_D = "R&D"
    SUPPORT = "Support"
    LEGAL = "Legal"
    CONSULTING = "Consulting"
    EDUCATION = "Education"
    ARTS_AND_DESIGN = "Arts and Design"
    ADMINISTRATIVE = "Administrative"
    ACCOUNTING = "Accounting"
    HEALTHCARE_SERVICES = "Healthcare Services"
    MEDIA_AND_COMMUNICATION = "Media and Communication"
    COMMUNITY_AND_SOCIAL_SERVICES = "Community and Social Services"
    MILITARY_AND_PROTECTIVE_SERVICES = "Military and Protective Services"
    QUALITY_ASSURANCE = "Quality Assurance"
    REAL_ESTATE = "Real Estate"
    PRODUCT_MANAGEMENT = "Product Management"
    PURCHASING = "Purchasing"
    BUSINESS_DEVELOPMENT = "Business Development"


class WebSearchSource(StrEnum):
    """Valid sources for web search API."""

    NEWS = "news"
    WEB = "web"
    SCHOLAR_ARTICLES = "scholar-articles"
    SCHOLAR_ARTICLES_ENRICHED = "scholar-articles-enriched"
    SCHOLAR_AUTHOR = "scholar-author"
    AI = "ai"
    SOCIAL = "social"


# ============================================================================
# PersonDB Search Field Names
# ============================================================================

# Valid column names for PersonDB search filters (use with "column" key, not "filter_type")
PERSON_DB_VALID_COLUMNS: set[str] = {
    # Core person fields
    "person_id",
    "name",
    "first_name",
    "last_name",
    "region",
    "headline",
    "summary",
    "skills",
    "languages",
    "profile_language",
    "linkedin_profile_url",
    "flagship_profile_url",
    "emails",
    "twitter_handle",
    "num_of_connections",
    "recently_changed_jobs",
    "years_of_experience",
    "years_of_experience_raw",
    "last_updated",
    "updated_at",
    # Current employer fields
    "current_employers.name",
    "current_employers.title",
    "current_employers.company_id",
    "current_employers.linkedin_id",
    "current_employers.company_linkedin_id",
    "current_employers.company_website_domain",
    "current_employers.location",
    "current_employers.start_date",
    "current_employers.seniority_level",
    "current_employers.function_category",
    "current_employers.years_at_company",
    "current_employers.years_at_company_raw",
    "current_employers.company_headquarters_country",
    "current_employers.company_hq_location",
    "current_employers.company_headcount_range",
    "current_employers.company_headcount_latest",
    "current_employers.company_industries",
    "current_employers.company_linkedin_industry",
    "current_employers.company_type",
    # Past employer fields
    "past_employers.name",
    "past_employers.title",
    "past_employers.company_id",
    "past_employers.linkedin_id",
    "past_employers.company_linkedin_id",
    "past_employers.company_website_domain",
    "past_employers.location",
    "past_employers.start_date",
    "past_employers.end_date",
    "past_employers.seniority_level",
    "past_employers.function_category",
    "past_employers.years_at_company",
    "past_employers.years_at_company_raw",
    "past_employers.company_headquarters_country",
    "past_employers.company_hq_location",
    "past_employers.company_headcount_range",
    "past_employers.company_headcount_latest",
    "past_employers.company_industries",
    "past_employers.company_linkedin_industry",
    "past_employers.company_type",
    # All employers fields (combined)
    "all_employers.name",
    "all_employers.title",
    "all_employers.company_id",
    "all_employers.start_date",
    "all_employers.end_date",
    # Education fields
    "education_background.institute_name",
    "education_background.degree_name",
    "education_background.field_of_study",
    "education_background.start_date",
    "education_background.end_date",
    # Certification fields
    "certifications.name",
    "certifications.issuer_organization",
}


def validate_person_db_columns(columns: list[str]) -> tuple[list[str], list[str]]:
    """Validate PersonDB column names.

    Args:
        columns: List of column names to validate

    Returns:
        Tuple of (valid_columns, invalid_columns)
    """
    valid = []
    invalid = []
    for col in columns:
        if col in PERSON_DB_VALID_COLUMNS:
            valid.append(col)
        else:
            invalid.append(col)
    return valid, invalid


class PostType(StrEnum):
    """Valid LinkedIn post types."""

    ORIGINAL = "original"
    REPOST = "repost"


class WatcherEventType(StrEnum):
    """Valid event types for watcher subscriptions."""

    JOB_POSTING_WITH_KEYWORD_AND_LOCATION = "job-posting-with-keyword-and-location"
    COMPANY_WATCH_LINKEDIN_POSTS = "company-watch-linkedin-posts"
    LINKEDIN_PERSON_POST_UPDATES = "linkedin-person-post-updates"


class WatcherStatus(StrEnum):
    """Valid status values for watcher subscriptions."""

    ACTIVE = "active"
    PAUSED = "paused"
    CANCELLED = "cancelled"


class FilterOperator(StrEnum):
    """Valid filter operators for search and watcher filters.

    Operators vary by context:
    - DB searches: =, !=, >, <, >=, <=, in, not_in, (.), [.]
    - Real-time searches: in, not in, between
    - Watcher filters: in, =, !=
    """

    # Equality operators
    EQUALS = "="
    NOT_EQUALS = "!="

    # Comparison operators
    GREATER_THAN = ">"
    LESS_THAN = "<"
    GREATER_THAN_OR_EQUAL = ">="
    LESS_THAN_OR_EQUAL = "<="

    # Set membership operators
    IN = "in"
    NOT_IN = "not_in"
    NOT_IN_SPACE = "not in"  # Alternative format used in real-time searches

    # Pattern matching operators
    FUZZY = "(.)"
    REGEX = "[.]"

    # Range operator (for real-time searches)
    BETWEEN = "between"

    # Contains operator
    CONTAINS = "contains"


class CompanyEnrichField(StrEnum):
    """Valid field names for company enrichment API.

    These are the top-level fields that can be requested via the `fields` parameter
    of the crustdata_company_enrich tool. Using this enum prevents LLMs from
    guessing invalid field names like 'employee_count' or 'short_description'.

    Common fields:
    - COMPANY_NAME, COMPANY_ID: Basic identifiers
    - LINKEDIN_PROFILE_URL: LinkedIn company page URL
    - LINKEDIN_COMPANY_DESCRIPTION: Company description from LinkedIn
    - HEADCOUNT: Employee metrics (nested object with linkedin_headcount, etc.)
    - FUNDING_AND_INVESTMENT: Funding data (nested object)
    - HQ_COUNTRY, HEADQUARTERS: Location data
    """

    # Basic Company Information
    COMPANY_ID = "company_id"
    COMPANY_NAME = "company_name"
    LINKEDIN_PROFILE_NAME = "linkedin_profile_name"
    LINKEDIN_PROFILE_URL = "linkedin_profile_url"
    CRUNCHBASE_PROFILE_URL = "crunchbase_profile_url"
    CRUNCHBASE_PROFILE_UUID = "crunchbase_profile_uuid"
    LINKEDIN_ID = "linkedin_id"
    LINKEDIN_LOGO_URL = "linkedin_logo_url"
    LINKEDIN_LOGO_PERMALINK = "linkedin_logo_permalink"
    COMPANY_TWITTER_URL = "company_twitter_url"
    COMPANY_WEBSITE_DOMAIN = "company_website_domain"
    COMPANY_WEBSITE = "company_website"
    DOMAINS = "domains"
    IS_FULL_DOMAIN_MATCH = "is_full_domain_match"

    # Firmographics
    HQ_COUNTRY = "hq_country"
    HEADQUARTERS = "headquarters"
    LARGEST_HEADCOUNT_COUNTRY = "largest_headcount_country"
    HQ_STREET_ADDRESS = "hq_street_address"
    YEAR_FOUNDED = "year_founded"
    FISCAL_YEAR_END = "fiscal_year_end"
    ESTIMATED_REVENUE_LOWER_BOUND_USD = "estimated_revenue_lower_bound_usd"
    ESTIMATED_REVENUE_HIGHER_BOUND_USD = "estimated_revenue_higher_bound_usd"
    ESTIMATED_REVENUE_TIMESERIES = "estimated_revenue_timeseries"
    EMPLOYEE_COUNT_RANGE = "employee_count_range"
    COMPANY_TYPE = "company_type"
    LINKEDIN_COMPANY_DESCRIPTION = "linkedin_company_description"
    ACQUISITION_STATUS = "acquisition_status"
    IPO_DATE = "ipo_date"
    MARKETS = "markets"
    STOCK_SYMBOLS = "stock_symbols"
    CEO_LOCATION = "ceo_location"
    ALL_OFFICE_ADDRESSES = "all_office_addresses"

    # Taxonomy and Categories
    TAXONOMY = "taxonomy"

    # Competitors
    COMPETITORS = "competitors"

    # Headcount (nested object with detailed employee metrics)
    HEADCOUNT = "headcount"

    # Web Traffic
    WEB_TRAFFIC = "web_traffic"

    # Review Platforms
    GLASSDOOR = "glassdoor"
    G2 = "g2"
    GARTNER = "gartner"

    # LinkedIn Followers
    LINKEDIN_FOLLOWERS = "linkedin_followers"

    # Funding and Investment
    FUNDING_AND_INVESTMENT = "funding_and_investment"

    # Job Openings
    JOB_OPENINGS = "job_openings"

    # SEO Metrics
    SEO = "seo"

    # People Data
    FOUNDERS = "founders"
    CXOS = "cxos"
    DECISION_MAKERS = "decision_makers"

    # News and Product Hunt
    NEWS_ARTICLES = "news_articles"
    PRODUCTHUNT = "producthunt"


# Valid CompanyEnrichField values as a set for fast lookup
COMPANY_ENRICH_VALID_FIELDS: set[str] = {f.value for f in CompanyEnrichField}


def validate_company_enrich_fields(fields: list[str]) -> tuple[list[str], list[str]]:
    """Validate company enrichment field names against allowed values.

    Args:
        fields: List of field names to validate.

    Returns:
        Tuple of (valid_fields, invalid_fields).
    """
    valid = []
    invalid = []
    for field in fields:
        if field in COMPANY_ENRICH_VALID_FIELDS:
            valid.append(field)
        else:
            invalid.append(field)
    return valid, invalid


# ============================================================================
# Validator Helpers
# ============================================================================


def validate_linkedin_url(url: str) -> str:
    """Validate LinkedIn URL format.

    Args:
        url: LinkedIn URL to validate

    Returns:
        The validated URL

    Raises:
        ValueError: If URL doesn't match expected LinkedIn format
    """
    if not url.startswith(("https://linkedin.com/", "https://www.linkedin.com/")):
        raise ValueError(
            f"Invalid LinkedIn URL: {url}. Must start with https://linkedin.com/ or https://www.linkedin.com/"
        )
    return url


def validate_domain(domain: str) -> str:
    """Validate domain format (lowercase, no protocol).

    Args:
        domain: Domain to validate

    Returns:
        The validated domain in lowercase

    Raises:
        ValueError: If domain contains protocol
    """
    if "://" in domain:
        raise ValueError(f"Domain should not include protocol: {domain}. Use 'example.com' format.")
    return domain.lower()


def validate_webhook_url(url: str) -> str:
    """Validate webhook URL is HTTPS.

    Args:
        url: Webhook URL to validate

    Returns:
        The validated URL

    Raises:
        ValueError: If URL is not HTTPS
    """
    if not url.startswith("https://"):
        raise ValueError(f"Webhook URL must use HTTPS: {url}")
    return url


def validate_iso_date(date_str: str) -> str:
    """Validate ISO 8601 date format (YYYY-MM-DD).

    Args:
        date_str: Date string to validate

    Returns:
        The validated date string

    Raises:
        ValueError: If date doesn't match YYYY-MM-DD format
    """
    from datetime import datetime

    try:
        datetime.strptime(date_str, "%Y-%m-%d")
    except ValueError as e:
        raise ValueError(f"Invalid ISO date format: {date_str}. Expected YYYY-MM-DD.") from e
    return date_str


def validate_future_date(date_str: str) -> str:
    """Validate that date is in the future.

    Args:
        date_str: Date string in YYYY-MM-DD format

    Returns:
        The validated date string

    Raises:
        ValueError: If date is not in the future
    """
    from datetime import datetime

    date_str = validate_iso_date(date_str)
    date_obj = datetime.strptime(date_str, "%Y-%m-%d").date()
    today = datetime.now().date()

    if date_obj <= today:
        raise ValueError(f"Date must be in the future: {date_str}")
    return date_str


# ============================================================================
# Filter Models
# ============================================================================


class FilterCondition(BaseModel):
    """A single filter condition for search queries.

    Used in both company and people search APIs to filter results
    based on various criteria.
    """

    filter_type: str = Field(..., description="The type of filter (e.g., 'hq_country', 'industry')")
    type: Literal["=", "!=", ">", "<", ">=", "<=", "in", "not_in", "contains"] = Field(
        "=", description="The comparison operator"
    )
    value: Any = Field(..., description="The filter value")


class FilterGroup(BaseModel):
    """A group of filter conditions with logical operators.

    Allows combining multiple filters with AND/OR logic.
    """

    op: Literal["AND", "OR"] = Field("AND", description="Logical operator for combining conditions")
    conditions: list[Union[FilterCondition, FilterGroup]] = Field(
        default_factory=list, description="List of filter conditions or nested groups"
    )


class SortOption(BaseModel):
    """Sort option for search results."""

    column: str = Field(..., description="Column/field to sort by")
    order: Literal["asc", "desc"] = Field("asc", description="Sort order")


class PaginationParams(BaseModel):
    """Pagination parameters for list/search endpoints."""

    page: int | None = Field(None, ge=1, description="Page number (1-indexed)")
    limit: int | None = Field(None, ge=1, le=1000, description="Number of results per page")
    cursor: str | None = Field(None, description="Cursor for cursor-based pagination")
    offset: int | None = Field(None, ge=0, description="Number of results to skip")


class ApiResponse(BaseModel):
    """Standard API response wrapper."""

    success: bool = Field(True, description="Whether the request was successful")
    data: Any = Field(None, description="Response data")
    message: str | None = Field(None, description="Optional message")
    total_count: int | None = Field(None, description="Total count for paginated results")
    next_cursor: str | None = Field(None, description="Next cursor for pagination")


# Re-export for convenience
FilterGroup.model_rebuild()  # Required for self-referencing model
