#!/usr/bin/env python3

import os
import sys
import time
import logging
from datetime import datetime, timezone
from typing import Dict, Generator, Optional
from dotenv import load_dotenv
load_dotenv()

import requests
import mysql.connector
from mysql.connector import Error as MySQLError

# =========================
# CONFIG
# =========================
API_KEY = os.getenv("MASSIVE_API_KEY", "").strip()

MYSQL_HOST = os.getenv("MYSQL_HOST", "127.0.0.1")
MYSQL_PORT = int(os.getenv("MYSQL_PORT", "3306"))
MYSQL_DATABASE = os.getenv("MYSQL_DATABASE", "").strip()
MYSQL_USER = os.getenv("MYSQL_USER", "").strip()
MYSQL_PASSWORD = os.getenv("MYSQL_PASSWORD", "").strip()

BASE_URL = "https://api.massive.com"
REQUEST_TIMEOUT = 30
PAGE_LIMIT = 1000

LOGO_DIR = "logos"
LOGO_PUBLIC_BASE_URL = "https://api.tprofit.io/us_market/logos"

# ?? Fallback sources
FMP_LOGO_URL = "https://financialmodelingprep.com/image-stock/{symbol}.png"
IEX_LOGO_URL = "https://storage.googleapis.com/iex/api/logos/{symbol}.png"

# Delay tuning
DETAIL_DELAY_SECONDS = 0.02
NEWS_DELAY_SECONDS = 0.02
LOGO_DELAY_SECONDS = 0.005

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s | %(levelname)s | %(message)s"
)
logger = logging.getLogger("massive_import")


# =========================
# HELPERS
# =========================
def utc_now_mysql():
    return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")


def ensure_dirs():
    os.makedirs(LOGO_DIR, exist_ok=True)


def make_session():
    s = requests.Session()
    s.headers.update({
        "Authorization": f"Bearer {API_KEY}",
        "Accept": "application/json",
    })
    return s


def sanitize_symbol(symbol: str):
    return "".join(c if c.isalnum() or c in "-_." else "_" for c in symbol)


def get_logo_local_path(symbol):
    return os.path.join(LOGO_DIR, f"{sanitize_symbol(symbol)}.png")


def get_logo_public_url(symbol):
    return f"{LOGO_PUBLIC_BASE_URL}/{sanitize_symbol(symbol)}.png"


# =========================
# MASSIVE API
# =========================
def iter_all_symbols(session):
    url = f"{BASE_URL}/v3/reference/tickers"
    params = {"market": "stocks", "active": "true", "limit": PAGE_LIMIT}

    while True:
        r = session.get(url, params=params, timeout=REQUEST_TIMEOUT)
        r.raise_for_status()
        data = r.json()

        for row in data.get("results", []):
            yield row

        if not data.get("next_url"):
            break

        url = data["next_url"]
        params = None


def get_overview(session, symbol):
    url = f"{BASE_URL}/v3/reference/tickers/{symbol}"
    r = session.get(url, timeout=REQUEST_TIMEOUT)
    r.raise_for_status()
    return r.json().get("results", {})


def get_news(session, symbol):
    url = f"{BASE_URL}/v2/reference/news"
    params = {"ticker": symbol, "limit": 1}

    try:
        r = session.get(url, params=params, timeout=REQUEST_TIMEOUT)
        r.raise_for_status()
        results = r.json().get("results", [])
        return results[0].get("article_url") if results else None
    except:
        return None


def extract_logo(overview):
    branding = overview.get("branding") or {}
    return branding.get("logo_url") or branding.get("icon_url")


# =========================
# LOGO DOWNLOAD
# =========================
def download_logo(session, url, symbol):
    try:
        r = session.get(url, timeout=REQUEST_TIMEOUT)
        if r.status_code != 200:
            return None

        path = get_logo_local_path(symbol)
        with open(path, "wb") as f:
            f.write(r.content)

        return get_logo_public_url(symbol)

    except:
        return None


def download_logo_with_fallback(session, overview, symbol):
    # 1?? Massive
    logo = extract_logo(overview)
    if logo:
        result = download_logo(session, logo, symbol)
        if result:
            return result

    # 2?? FMP (BEST fallback)
    result = download_logo(session, FMP_LOGO_URL.format(symbol=symbol), symbol)
    if result:
        return result

    # 3?? IEX
    return download_logo(session, IEX_LOGO_URL.format(symbol=symbol), symbol)


# =========================
# MYSQL
# =========================
def connect_db():
    return mysql.connector.connect(
        host=MYSQL_HOST,
        port=MYSQL_PORT,
        database=MYSQL_DATABASE,
        user=MYSQL_USER,
        password=MYSQL_PASSWORD
    )


def upsert(conn, name, symbol, logo, news_url):
    sql = """
    INSERT INTO us_stocks
    (name, symbol, logo, price, status, news_url, created_at, updated_at)
    VALUES (%s,%s,%s,%s,%s,%s,%s,%s)
    ON DUPLICATE KEY UPDATE
    name=VALUES(name),
    logo=VALUES(logo),
    news_url=VALUES(news_url),
    updated_at=VALUES(updated_at)
    """

    now = utc_now_mysql()

    cur = conn.cursor()
    cur.execute(sql, (
        name,
        symbol,
        logo,
        None,
        "active",
        news_url,
        now,
        now
    ))
    conn.commit()


# =========================
# MAIN
# =========================
def main():
    ensure_dirs()
    session = make_session()
    conn = connect_db()

    count = 0

    for item in iter_all_symbols(session):
        symbol = item.get("ticker")
        if not symbol:
            continue

        name = item.get("name") or symbol

        try:
            overview = get_overview(session, symbol)
            name = overview.get("name") or name
        except:
            overview = {}

        logo = download_logo_with_fallback(session, overview, symbol)
        news = get_news(session, symbol)

        try:
            upsert(conn, name, symbol, logo, news)
        except Exception as e:
            logger.error(f"DB error {symbol}: {e}")

        count += 1
        if count % 200 == 0:
            logger.info(f"Processed {count}")

        time.sleep(0.01)

    logger.info("DONE")


if __name__ == "__main__":
    main()