"""Internal utility functions for tidyfinance."""

import numpy as np
import pandas as pd


def _assign_exchange(primaryexch):
    """
    Assign exchange for CRSP data.

    Parameters
    ----------
        primaryexch (str): A string of exchange letter.

    Returns
    -------
        str: Exchange name.
    """
    if primaryexch == "N":
        return "NYSE"
    elif primaryexch == "A":
        return "AMEX"
    elif primaryexch == "Q":
        return "NASDAQ"
    else:
        return "Other"


def _assign_industry(siccd):
    """
    Assign industry for CRSP data.

    Parameters
    ----------
        siccd (int): An integer to present the siccd.

    Returns
    -------
        str: Industry name.
    """
    if 1 <= siccd <= 999:
        return "Agriculture"
    elif 1000 <= siccd <= 1499:
        return "Mining"
    elif 1500 <= siccd <= 1799:
        return "Construction"
    elif 2000 <= siccd <= 3999:
        return "Manufacturing"
    elif 4000 <= siccd <= 4899:
        return "Transportation"
    elif 4900 <= siccd <= 4999:
        return "Utilities"
    elif 5000 <= siccd <= 5199:
        return "Wholesale"
    elif 5200 <= siccd <= 5999:
        return "Retail"
    elif 6000 <= siccd <= 6799:
        return "Finance"
    elif 7000 <= siccd <= 8999:
        return "Services"
    elif 9000 <= siccd <= 9999:
        return "Public"
    else:
        return "Missing"


def _parse_date(d: str, is_end: bool = False) -> pd.Timestamp:
        """Parse YYYY-MM-DD or YYYYMM into a Timestamp."""
        if d is None:
            return None
        d = str(d)
        if len(d) == 6 and d.isdigit():  # YYYYMM
            ts = pd.to_datetime(d, format="%Y%m")
            if is_end:
                # Move to last day of the month
                ts = ts + pd.offsets.MonthEnd(0)
            return ts.normalize()
        return pd.to_datetime(d).normalize().date()


def _validate_dates(
    start_date: str = None,
    end_date: str = None,
    use_default_range: bool = False,
) -> tuple[pd.Timestamp | None, pd.Timestamp | None]:
    """
    Validate and process start and end dates.

    Parameters
    ----------
    start_date : str, optional
        The start date in "YYYY-MM-DD" format.
    end_date : str, optional
        The end date in "YYYY-MM-DD" format.
    use_default_range : bool, optional
        Whether to use a default date range if no dates are provided.

    Returns
    -------
    tuple
        A tuple containing the validated start and end dates.
    """
    if start_date is None and end_date is None:
        if use_default_range:
            end_date = pd.Timestamp.today().normalize()
            start_date = end_date - pd.DateOffset(years=2)
            print(
                "No start_date or end_date provided. Using the range "
                f"{start_date.date()} to {end_date.date()} to avoid "
                "downloading large amounts of data."
            )
            return start_date.date(), end_date.date()
        else:
            print("No start_date or end_date provided. "
                  "Returning the full dataset."
            )
            return None, None

    start_date = _parse_date(start_date, is_end=False) if start_date else None
    end_date = _parse_date(end_date, is_end=True) if end_date else None

    # If only one date is provided, fill the other sensibly
    if start_date is None:
        # If only end_date is given, leave start open
        return None, end_date
    if end_date is None:
        # If only start_date is given, leave end open
        return start_date, None

    if start_date > end_date:
        raise ValueError("start_date cannot be after end_date.")
    return start_date, end_date


def _format_cusips(cusips):
    if not cusips:
        return "()"

    cusip_batch_formatted = ", ".join(f"'{cusip}'" for cusip in cusips)
    return f"({cusip_batch_formatted})"


def _return_datetime(dates):
    """Return date without time and change period to timestamp."""
    return pd.to_datetime(dates.astype(str))


def _transfrom_to_snake_case(column_name):
    """
    Convert a string to snake_case.

    - Converts uppercase letters to lowercase.
    - Replaces spaces and special characters with underscores.
    - Ensures no multiple underscores.
    """
    column_name = column_name.replace(" ", "_").replace("-", "_").lower()
    column_name = "".join(
        c if c.isalnum() or c == "_" else "_" for c in column_name
    )

    while "__" in column_name:
        column_name = column_name.replace("__", "_")

    return column_name.strip("_")


def _get_random_user_agent():
    """Retrieve a random user agent string.

    Returns
    -------
        str: A random user agent string.
    """
    user_agents = [
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246",
        "Mozilla/5.0 (X11; CrOS x86_64 8172.45.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.64 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36",
        "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1",
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15",
        "Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36",
        "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1",
        "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0",
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.110 Safari/537.36",
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:117.0) Gecko/20100101 Firefox/117.0",
        "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:116.0) Gecko/20100101 Firefox/116.0",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.141 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_6_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_7_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.110 Safari/537.36 Edg/116.0.1938.69",
    ]
    return str(np.random.choice(user_agents))
