"""Company tools for Crustdata MCP Server."""

from typing import Optional

from mcp.server.fastmcp import Context
from pydantic import BaseModel, Field, field_validator, model_validator

from ..client import CrustdataAPIError, CrustdataAuthError, CrustdataNotFoundError
from ..client_factory import get_client_for_request
from ..models.common import (
    COMPANY_ENRICH_VALID_FIELDS,
    validate_company_enrich_fields,
    validate_domain,
    validate_linkedin_url,
)
from ..server import AppContext, mcp


class CompanyIdentifyInput(BaseModel):
    """Input parameters for company identification."""

    company_name: Optional[str] = Field(
        None,
        description="Company name to search for (e.g., 'Anthropic'). Can be comma-separated for multiple.",
    )
    company_website: Optional[str] = Field(
        None,
        description="Company website domain (e.g., 'anthropic.com'). Can be comma-separated for multiple.",
    )
    company_linkedin_url: Optional[str] = Field(
        None,
        description="LinkedIn company page URL (e.g., 'https://linkedin.com/company/anthropic').",
    )
    company_crunchbase_url: Optional[str] = Field(
        None,
        description="Crunchbase company URL.",
    )
    count: int = Field(
        10,
        ge=1,
        le=25,
        description="Maximum number of matches to return per query (default: 10, max: 25).",
    )
    exact_match: bool = Field(
        False,
        description="If true, only return exact matches. Default is fuzzy matching.",
    )

    company_id: Optional[str] = Field(
        None,
        description="Crustdata company ID.",
    )

    @field_validator("company_linkedin_url")
    @classmethod
    def validate_linkedin_url_format(cls, v: Optional[str]) -> Optional[str]:
        """Validate LinkedIn URL format."""
        if v is not None:
            return validate_linkedin_url(v)
        return v

    @model_validator(mode="after")
    def check_at_least_one_identifier(self) -> "CompanyIdentifyInput":
        """Ensure at least one identifier is provided."""
        if not any([
            self.company_name,
            self.company_website,
            self.company_linkedin_url,
            self.company_crunchbase_url,
            self.company_id,
        ]):
            raise ValueError(
                "At least one identifier required: company_name, company_website, "
                "company_linkedin_url, company_crunchbase_url, or company_id"
            )
        return self


@mcp.tool(
    name="crustdata_company_identify",
    description="Identify and match companies by name, domain, LinkedIn URL, or Crunchbase URL. "
    "Returns matching company profiles with basic information. "
    "Use this to find the Crustdata company ID for further enrichment.",
)
async def crustdata_company_identify(
    params: CompanyIdentifyInput,
    ctx: Context[None, AppContext, None],
) -> list[dict]:
    """Identify companies by name, domain, or URL.

    Returns a list of matching companies with basic profile information.
    Use the returned company_id for detailed enrichment with crustdata_company_enrich.

    Args:
        params: Company identification parameters (at least one identifier required).
        ctx: MCP context for logging and client access.

    Returns:
        List of matching company profiles with fields like:
        - company_id: Unique identifier for the company
        - company_name: Official company name
        - company_website_domain: Primary domain
        - linkedin_profile_url: LinkedIn company page
        - employee_count_range: Estimated employee range
        - is_full_domain_match: Whether the match was exact
    """
    client = get_client_for_request()

    # Build request data with query_ prefix
    data: dict = {}
    if params.company_name:
        data["query_company_name"] = params.company_name
    if params.company_website:
        data["query_company_website"] = params.company_website
    if params.company_linkedin_url:
        data["query_company_linkedin_url"] = params.company_linkedin_url
    if params.company_crunchbase_url:
        data["query_company_crunchbase_url"] = params.company_crunchbase_url
    if params.company_id:
        data["query_company_id"] = params.company_id

    data["count"] = params.count
    data["exact_match"] = params.exact_match

    try:
        ctx.info(f"Identifying company with params: {data}")
        result = await client.post("/screener/identify/", data=data)

        # Result is a list of matching companies
        if isinstance(result, list):
            ctx.info(f"Found {len(result)} matching companies")
            return result
        else:
            # Handle unexpected response format
            ctx.warning(f"Unexpected response format: {type(result)}")
            return [result] if result else []

    except CrustdataNotFoundError:
        ctx.info("No matching companies found")
        return []
    except CrustdataAuthError as e:
        ctx.error(f"Authentication failed: {e}")
        raise
    except CrustdataAPIError as e:
        ctx.error(f"API error: {e}")
        raise


class CompanyEnrichInput(BaseModel):
    """Input parameters for company enrichment."""

    company_domain: Optional[str] = Field(
        None,
        description="Company domain (e.g., 'anthropic.com'). Can be comma-separated for multiple (max 25).",
    )
    company_name: Optional[str] = Field(
        None,
        description="Company name. Can be comma-separated for multiple (max 25).",
    )
    company_linkedin_url: Optional[str] = Field(
        None,
        description="LinkedIn company page URL. Can be comma-separated for multiple (max 25).",
    )
    company_id: Optional[str] = Field(
        None,
        description="Crustdata company ID. Can be comma-separated for multiple (max 25).",
    )
    fields: Optional[list[str]] = Field(
        None,
        description="""Specific fields to return. If not specified, returns all available fields.

Valid field names include:
- Basic: company_id, company_name, linkedin_profile_url, company_website_domain
- Location: hq_country, headquarters, all_office_addresses
- Description: linkedin_company_description (NOT 'description' or 'short_description')
- Employee data: headcount, employee_count_range (NOT 'employee_count')
- Financial: funding_and_investment, estimated_revenue_lower_bound_usd
- Social: linkedin_followers, glassdoor, g2
- People: founders, cxos, decision_makers
- Other: taxonomy, competitors, job_openings, seo, web_traffic, news_articles
""",
    )
    enrich_realtime: bool = Field(
        False,
        description="Force real-time enrichment (slower but more current data). "
        "Uses more credits. Default: false (uses cached data).",
    )
    exact_match: bool = Field(
        False,
        description="If true, only return exact matches for name/domain lookup.",
    )

    @field_validator("fields")
    @classmethod
    def validate_fields(cls, v: Optional[list[str]]) -> Optional[list[str]]:
        """Validate fields against allowed CompanyEnrichField enum values."""
        if v is None:
            return v

        valid_fields, invalid_fields = validate_company_enrich_fields(v)

        if invalid_fields:
            # Get some examples of valid fields to help the user
            example_fields = sorted(list(COMPANY_ENRICH_VALID_FIELDS))[:10]
            raise ValueError(
                f"Invalid fields provided: {', '.join(invalid_fields)}. "
                f"Valid fields include: {', '.join(example_fields)}... "
                f"(total {len(COMPANY_ENRICH_VALID_FIELDS)} valid fields)"
            )

        return v

    @field_validator("company_domain")
    @classmethod
    def validate_domain_format(cls, v: Optional[str]) -> Optional[str]:
        """Validate domain format for single or comma-separated domains."""
        if v is not None:
            # Handle comma-separated domains
            domains = [d.strip() for d in v.split(",")]
            validated = [validate_domain(d) for d in domains]
            return ",".join(validated)
        return v

    @field_validator("company_linkedin_url")
    @classmethod
    def validate_linkedin_url_format(cls, v: Optional[str]) -> Optional[str]:
        """Validate LinkedIn URL format for single or comma-separated URLs."""
        if v is not None:
            # Handle comma-separated URLs
            urls = [u.strip() for u in v.split(",")]
            validated = [validate_linkedin_url(u) for u in urls]
            return ",".join(validated)
        return v

    @model_validator(mode="after")
    def check_at_least_one_identifier(self) -> "CompanyEnrichInput":
        """Ensure at least one identifier is provided."""
        if not any([
            self.company_domain,
            self.company_name,
            self.company_linkedin_url,
            self.company_id,
        ]):
            raise ValueError(
                "At least one identifier required: company_domain, company_name, "
                "company_linkedin_url, or company_id"
            )
        return self


@mcp.tool(
    name="crustdata_company_enrich",
    description="Get comprehensive company profile data including employee metrics, "
    "funding data, social profiles, location data, and industry classification. "
    "Costs 1 credit (default) or 4 credits (realtime enrichment).",
)
async def crustdata_company_enrich(
    params: CompanyEnrichInput,
    ctx: Context[None, AppContext, None],
) -> dict:
    """Get detailed company profile data.

    Returns comprehensive company information including:
    - Basic info (name, website, founded date)
    - Employee metrics (headcount, growth trends)
    - Funding data (total raised, last round details)
    - Social profiles (LinkedIn, Twitter)
    - Location data (headquarters, offices)
    - Industry classification

    Args:
        params: Company enrichment parameters (at least one identifier required).
        ctx: MCP context for logging and client access.

    Returns:
        Dictionary with company profile data.
    """
    client = get_client_for_request()

    # Build query params
    query_params: dict = {}
    if params.company_domain:
        query_params["company_domain"] = params.company_domain
    if params.company_name:
        query_params["company_name"] = params.company_name
    if params.company_linkedin_url:
        query_params["company_linkedin_url"] = params.company_linkedin_url
    if params.company_id:
        query_params["company_id"] = params.company_id
    if params.fields:
        query_params["fields"] = ",".join(params.fields)
    if params.enrich_realtime:
        query_params["enrich_realtime"] = "true"
    if params.exact_match:
        query_params["exact_match"] = "true"

    try:
        ctx.info(f"Enriching company with params: {query_params}")
        result = await client.get("/screener/company", params=query_params)

        # Handle single vs multiple results
        if isinstance(result, list):
            ctx.info(f"Found {len(result)} companies")
            return {"companies": result, "count": len(result)}
        else:
            ctx.info("Found 1 company")
            return result

    except CrustdataNotFoundError:
        ctx.info("Company not found")
        return {"found": False, "message": "Company not found in database"}
    except CrustdataAuthError as e:
        ctx.error(f"Authentication failed: {e}")
        raise
    except CrustdataAPIError as e:
        ctx.error(f"API error: {e}")
        raise


class CompanySearchInput(BaseModel):
    """Input parameters for company database search."""

    filters: dict = Field(
        ...,
        description="""Search filters. Can be a simple filter or complex nested conditions.

Simple filter example:
{"filter_type": "company_name", "type": "(.)", "value": "Anthropic"}

Complex AND filter example:
{
    "op": "AND",
    "conditions": [
        {"filter_type": "hq_country", "type": "(.)", "value": "United States"},
        {"filter_type": "employee_metrics.latest_count", "type": ">", "value": 100}
    ]
}

Operators: =, !=, >, <, >=, <=, in, not_in, (.) (fuzzy), [.] (regex)
""",
    )
    sorts: Optional[list[dict]] = Field(
        None,
        description='Sort options. Example: [{"column": "employee_metrics.latest_count", "order": "desc"}]',
    )
    limit: int = Field(
        20,
        ge=1,
        le=1000,
        description="Number of results to return (default: 20, max: 1000).",
    )
    cursor: Optional[str] = Field(
        None,
        description="Pagination cursor from previous response for fetching next page.",
    )


@mcp.tool(
    name="crustdata_company_search_db",
    description="Search the Crustdata company database with flexible filters. "
    "Fast search against pre-indexed data. Supports filtering by name, location, "
    "employee count, funding, industry, and more. Costs 1 credit per search. "
    "NOTE: This uses database field names (e.g., 'company_name', 'hq_country'), "
    "NOT LinkedIn Sales Navigator filters (use crustdata_company_search for those).",
)
async def crustdata_company_search_db(
    params: CompanySearchInput,
    ctx: Context[None, AppContext, None],
) -> dict:
    """Search companies in the Crustdata database.

    Returns matching companies based on the provided filters. This tool searches
    pre-indexed database fields, which differ from LinkedIn Sales Navigator filters
    used in crustdata_company_search.

    Common filter fields:
        - company_name: Company name (string)
        - hq_country: HQ country code (ISO 3-alpha: USA, GBR, IND, etc.)
        - hq_location: Full HQ location string (string)
        - linkedin_industries: Array of industries (array)
        - employee_metrics.latest_count: Current employee count (number)
        - employee_metrics.growth_6m_percent: 6-month employee growth % (number)
        - company_website_domain: Primary domain (string)
        - last_funding_round_type: Funding type like "Series A", "Seed" (string)
        - crunchbase_total_investment_usd: Total funding raised (number)

    Filter operators:
        - = (equals), != (not equals)
        - >, <, >=, <= (comparisons for numbers)
        - in, not_in (array membership)
        - (.) (fuzzy/contains match)
        - [.] (regex match)

    Example filters:
        Simple fuzzy search:
        {"filter_type": "company_name", "type": "(.)", "value": "Anthropic"}

        Filter by country:
        {"filter_type": "hq_country", "type": "=", "value": "USA"}

        Employee count comparison:
        {"filter_type": "employee_metrics.latest_count", "type": ">", "value": 100}

        Complex AND filter:
        {
            "op": "AND",
            "conditions": [
                {"filter_type": "hq_country", "type": "=", "value": "USA"},
                {"filter_type": "employee_metrics.latest_count", "type": ">", "value": 100},
                {"filter_type": "linkedin_industries", "type": "in", "value": ["Software Development"]}
            ]
        }

    Args:
        params: Search parameters including filters, sorts, limit, and cursor.
        ctx: MCP context for logging and client access.

    Returns:
        Dictionary with:
        - companies: List of matching company profiles
        - total_count: Total number of matches
        - next_cursor: Cursor for fetching next page (if more results exist)
    """
    client = get_client_for_request()

    data: dict = {"filters": params.filters, "limit": params.limit}
    if params.sorts:
        data["sorts"] = params.sorts
    if params.cursor:
        data["cursor"] = params.cursor

    try:
        ctx.info(f"Searching companies with filters: {params.filters}")
        result = await client.post("/screener/companydb/search", data=data)

        companies = result.get("companies", [])
        ctx.info(f"Found {len(companies)} companies")
        return {
            "companies": companies,
            "total_count": result.get("total_count"),
            "next_cursor": result.get("next_cursor"),
        }

    except CrustdataAuthError as e:
        ctx.error(f"Authentication failed: {e}")
        raise
    except CrustdataAPIError as e:
        ctx.error(f"API error: {e}")
        raise


class LinkedInPostsSearchInput(BaseModel):
    """Input parameters for LinkedIn posts keyword search."""

    keyword: str = Field(
        ...,
        description="Keyword(s) to search for in LinkedIn posts.",
    )
    limit: int = Field(
        10,
        ge=1,
        le=100,
        description="Maximum number of posts to return (default: 10, max: 100).",
    )


@mcp.tool(
    name="crustdata_company_linkedin_posts",
    description="Search LinkedIn posts by keyword. "
    "Find company and personal posts mentioning specific topics, products, or trends. "
    "Useful for market research, competitive intelligence, and trend analysis.",
)
async def crustdata_company_linkedin_posts(
    params: LinkedInPostsSearchInput,
    ctx: Context[None, AppContext, None],
) -> list[dict]:
    """Search LinkedIn posts by keyword.

    Args:
        params: Search parameters including keyword and limit.
        ctx: MCP context for logging and client access.

    Returns:
        List of matching LinkedIn posts with content, author info, and engagement metrics.
    """
    client = get_client_for_request()

    data = {"keyword": params.keyword, "limit": params.limit}

    try:
        ctx.info(f"Searching LinkedIn posts for: {params.keyword}")
        result = await client.post("/screener/linkedin_posts/keyword_search/", data=data)

        if isinstance(result, list):
            ctx.info(f"Found {len(result)} posts")
            return result
        else:
            posts = result.get("posts", [])
            ctx.info(f"Found {len(posts)} posts")
            return posts

    except CrustdataAuthError as e:
        ctx.error(f"Authentication failed: {e}")
        raise
    except CrustdataAPIError as e:
        ctx.error(f"API error: {e}")
        raise


class CompanyAutocompleteInput(BaseModel):
    """Input parameters for company autocomplete."""

    field: str = Field(
        ...,
        description="Field to get autocomplete suggestions for (e.g., 'company_name', 'hq_country', 'linkedin_industries').",
    )
    query: str = Field(
        ...,
        description="Prefix/query to get suggestions for.",
    )
    limit: int = Field(
        20,
        ge=1,
        le=100,
        description="Maximum number of suggestions (default: 20, max: 100).",
    )


@mcp.tool(
    name="crustdata_autocomplete_company",
    description="Get autocomplete suggestions for company database fields. "
    "Useful for building filters or discovering valid field values.",
)
async def crustdata_autocomplete_company(
    params: CompanyAutocompleteInput,
    ctx: Context[None, AppContext, None],
) -> list[str]:
    """Get autocomplete suggestions for a company field.

    Args:
        params: Autocomplete parameters including field, query, and limit.
        ctx: MCP context for logging and client access.

    Returns:
        List of suggested values for the specified field.
    """
    client = get_client_for_request()

    data = {"field": params.field, "query": params.query, "limit": params.limit}

    try:
        ctx.info(f"Getting autocomplete for {params.field}: '{params.query}'")
        result = await client.post("/screener/companydb/autocomplete", data=data)

        suggestions = result.get("suggestions", [])
        ctx.info(f"Found {len(suggestions)} suggestions")
        return suggestions

    except CrustdataAuthError as e:
        ctx.error(f"Authentication failed: {e}")
        raise
    except CrustdataAPIError as e:
        ctx.error(f"API error: {e}")
        raise


# =============================================================================
# Real-time Company Search - POST /screener/company/search
# =============================================================================


class CompanySearchRealtimeInput(BaseModel):
    """Input parameters for real-time company search via LinkedIn Sales Navigator filters."""

    filters: list[dict] = Field(
        ...,
        description="""Array of filter objects for LinkedIn Sales Navigator style company filtering.

Each filter object should contain:
- filter_type: The type of filter (e.g., COMPANY_HEADCOUNT, INDUSTRY, REGION)
- type: The operation type - "in", "not in", or "between"
- value: The filter value(s) - array of strings, or object with min/max for range filters

Example filters:
[
  {"filter_type": "COMPANY_HEADCOUNT", "type": "in", "value": ["51-200", "201-500"]},
  {"filter_type": "INDUSTRY", "type": "in", "value": ["Software Development"]},
  {"filter_type": "COMPANY_HEADQUARTERS", "type": "in", "value": ["United States"]}
]

Common filter types:
- COMPANY_HEADCOUNT: 1-10, 11-50, 51-200, 201-500, 501-1,000, 1,001-5,000, 5,001-10,000, 10,001+
- COMPANY_HEADQUARTERS: Geographic locations
- INDUSTRY: Industry names
- REGION: Geographic regions
- ANNUAL_REVENUE: Revenue range (use "between" type with {"min": X, "max": Y})
- ACCOUNT_ACTIVITIES: e.g., "Funding events in past 12 months"

For valid filter_type values, see CompanySearchFilterType enum in models.common module.
""",
    )
    page: int = Field(
        1,
        ge=1,
        le=65,
        description="Page number for pagination (starts from 1, max 65). Each page returns up to 25 results.",
    )


@mcp.tool(
    name="crustdata_company_search",
    description="Real-time search for companies using LinkedIn Sales Navigator style filters. "
    "Find companies by headcount, industry, location, revenue, funding activity, and more. "
    "Returns up to 25 results per page.",
)
async def crustdata_company_search(
    params: CompanySearchRealtimeInput,
    ctx: Context[None, AppContext, None],
) -> dict:
    """Search for companies in real-time using advanced filters.

    Uses LinkedIn Sales Navigator style filtering for discovering companies.

    Args:
        params: Search parameters including filters array and page number.
        ctx: MCP context for logging and client access.

    Returns:
        Dictionary with:
        - companies: List of matching company profiles (up to 25 per page)
        - total_display_count: Total number of companies available (e.g., "1M+", "1.5K")
    """
    client = get_client_for_request()

    data: dict = {
        "filters": params.filters,
        "page": params.page,
    }

    try:
        ctx.info(f"Searching for companies with {len(params.filters)} filters on page {params.page}")
        result = await client.post("/screener/company/search", data=data)

        companies = result.get("companies", [])
        total_display_count = result.get("total_display_count", "unknown")
        ctx.info(f"Found {len(companies)} companies (total: {total_display_count})")

        return {
            "companies": companies,
            "total_display_count": total_display_count,
        }

    except CrustdataAuthError as e:
        ctx.error(f"Authentication failed: {e}")
        raise
    except CrustdataAPIError as e:
        ctx.error(f"API error: {e}")
        raise
