"""Utility functions for SEC bulk download toolkit."""

import lzma
import os
import pathlib
import urllib.request
from time import time
from typing import Any

from secbulkdownload.config import (
    binary_file_types,
    default_datadir,
    sec_rate_interval,
    sec_rate_limit,
)
from secbulkdownload.limiter import rate_limiter

# Instantiate rate_limiter
seclimiter = rate_limiter(sec_rate_interval, sec_rate_limit)


def timer(method: Any) -> Any:
    """Decorator to time function execution."""

    def wrapper(*args: Any, **kwargs: Any) -> Any:
        start_time = time()
        result = method(*args, **kwargs)
        end_time = time()
        elapsed = end_time - start_time
        if elapsed < 60:
            print("Time elapsed: ", elapsed, " seconds.")
        else:
            print("Time elapsed: ", elapsed / 60, " minutes.")
        return result

    return wrapper


def fetch_sec_file(
    url: str,
    localpath: str,
    limiter: rate_limiter = seclimiter,
    user_agent: str | None = None,
    content: bool = False,
    verbose: bool = False,
) -> str | tuple[str, bytes]:
    """
    Fetch a file from SEC and save it locally.

    Args:
        url: SEC URL to fetch
        localpath: Local path to save the file
        limiter: Rate limiter instance
        user_agent: Required SEC User-Agent header
        content: If True, return (localpath, data) tuple
        verbose: If True, print progress

    Returns:
        Local path or (localpath, data) tuple if content=True
    """
    # Setup request headers as required by SEC
    req = urllib.request.Request(url)
    if user_agent is not None:
        req.add_header("User-Agent", user_agent)
    else:
        raise ValueError(
            "The SEC requires a User-Agent definition of the form "
            "<Company Name admin@company.com>"
        )
    req.add_header("Host", "www.sec.gov")

    # Make sure that the directory path exists
    path = pathlib.Path(localpath)
    path.parent.mkdir(parents=True, exist_ok=True)

    try:
        if limiter.allow(verbose=verbose):
            with urllib.request.urlopen(req) as response:
                data = response.read()
                if url.split(".")[-1] in binary_file_types:
                    with open(localpath, "wb") as f:
                        f.write(data)
                else:
                    compressed_data = lzma.compress(data)
                    with open(localpath, "wb") as f:
                        f.write(compressed_data)

        if verbose:
            print(localpath)
        if content:
            return localpath, data
        else:
            return localpath

    except Exception as inst:
        print(url)
        print(localpath)
        print(inst)
        print(inst.args)
        raise


def get_ticker_name_dicts(
    datadir: str = default_datadir,
) -> tuple[dict[str, str], dict[str, str]]:
    """
    Get CIK to ticker and CIK to company name dictionaries.

    Args:
        datadir: Directory for caching company tickers file

    Returns:
        Tuple of (ticker_dict, name_dict) keyed by zero-padded 10-char CIK strings
    """
    import json
    from time import time as _time

    company_tickers_path = os.path.join(datadir, "company_tickers.json")

    # Decide whether to use the cached file
    use_cache = False
    if os.path.exists(company_tickers_path):
        mtime = os.path.getmtime(company_tickers_path)
        age_days = (_time() - mtime) / (60 * 60 * 24)
        if age_days < 1.0:
            use_cache = True

    if use_cache:
        with open(company_tickers_path) as f:
            raw: dict[str, Any] = json.load(f)
    else:
        # Fetch fresh data from SEC
        url = "https://www.sec.gov/files/company_tickers.json"
        req = urllib.request.Request(url)
        req.add_header("User-Agent", "secbulkdownload/1.0 (contact@example.com)")
        req.add_header("Host", "www.sec.gov")

        with urllib.request.urlopen(req) as response:
            data = response.read()

        raw = json.loads(data)

        # Ensure datadir exists and write cache
        pathlib.Path(company_tickers_path).parent.mkdir(parents=True, exist_ok=True)
        with open(company_tickers_path, "w") as f:
            json.dump(raw, f)

    # Build lookup dicts: zero-padded 10-char CIK → ticker / title
    ticker_dict: dict[str, str] = {}
    name_dict: dict[str, str] = {}
    for entry in raw.values():
        cik = str(entry["cik_str"]).zfill(10)
        ticker_dict[cik] = str(entry["ticker"])
        name_dict[cik] = str(entry["title"])

    return ticker_dict, name_dict
