import os
import json
import logging
from instagrapi import Client
from config.loader import CONFIG

ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


def configure_client(cl: Client, cookies: dict):
    cl.set_settings({"cookies": cookies})
    for k, v in cookies.items():
        cl.private.cookies.set(k, v, domain=".instagram.com")
        cl.public.cookies.set(k, v, domain=".instagram.com")


def test_session_validity(cl: Client, probe_tag: str) -> bool:
    try:
        tag_info = cl.hashtag_info(probe_tag)
        return bool(tag_info and tag_info.media_count)
    except Exception as e:
        logging.warning(f"Session probe failed on #{probe_tag}: {e}")
        return False


def _try_settings(cl: Client, settings_file: str, probe_tag: str, errors: list) -> bool:
    if not os.path.exists(settings_file):
        errors.append(f"session file not found: {settings_file}")
        return False
    try:
        logging.info(f"Loading session from {settings_file}...")
        cl.load_settings(settings_file)
        if test_session_validity(cl, probe_tag):
            logging.info("Device session successfully verified.")
            return True
        errors.append("saved session failed validity probe (expired or revoked)")
        logging.warning("Session settings failed validity probe.")
    except Exception as e:
        errors.append(f"could not load session settings: {e}")
        logging.warning(f"Could not load session settings: {e}")
    return False


def _try_cookies(cl: Client, cookie_file: str, settings_file: str, probe_tag: str, errors: list) -> bool:
    if not os.path.exists(cookie_file):
        errors.append(f"cookie file not found: {cookie_file}")
        return False
    try:
        logging.info(f"Injecting cookies from {cookie_file}...")
        with open(cookie_file, "r", encoding="utf-8") as f:
            cookies = json.load(f)
        configure_client(cl, cookies)
        if test_session_validity(cl, probe_tag):
            cl.dump_settings(settings_file)
            logging.info(f"Session valid and saved to {settings_file}.")
            return True
        errors.append("injected cookies failed validity probe (stale sessionid?)")
        logging.warning("Cookie injection failed validity probe.")
    except Exception as e:
        errors.append(f"could not use cookie file: {e}")
        logging.warning(f"Could not use cookie file: {e}")
    return False


def _try_browser_cookies(cl: Client, settings_file: str, probe_tag: str, errors: list) -> bool:
    """
    Extract instagram.com cookies automatically from locally installed browsers
    (Chrome, Firefox, Edge, Brave, Opera, Safari — first that yields a sessionid).
    No manual export needed.
    """
    try:
        import browser_cookie3
    except ImportError:
        errors.append("browser-cookie3 not installed (pip install browser-cookie3)")
        return False

    needed = ("sessionid", "ds_user_id", "csrftoken", "mid")
    browsers = [
        ("Chrome", browser_cookie3.chrome),
        ("Firefox", browser_cookie3.firefox),
        ("Edge", browser_cookie3.edge),
        ("Brave", browser_cookie3.brave),
        ("Opera", browser_cookie3.opera),
        ("Safari", browser_cookie3.safari),
    ]
    for name, loader in browsers:
        try:
            cj = loader(domain_name="instagram.com")
        except Exception as e:
            errors.append(f"{name}: unreadable cookie store ({type(e).__name__})")
            continue
        cookies = {c.name: c.value for c in cj if "instagram.com" in (c.domain or "")}
        found = {k: cookies[k] for k in needed if cookies.get(k)}
        if "sessionid" not in found:
            errors.append(f"{name}: no instagram sessionid found (not logged in?)")
            continue
        logging.info(f"Extracted Instagram cookies from {name} automatically.")
        configure_client(cl, found)
        if test_session_validity(cl, probe_tag):
            cl.dump_settings(settings_file)
            logging.info(f"Browser-session valid and saved to {settings_file}.")
            return True
        errors.append(f"{name}: cookies extracted but failed validity probe")
    return False


def _try_password_login(cl: Client, settings_file: str, errors: list) -> bool:
    """
    Last-resort login using IG_USERNAME / IG_PASSWORD env vars.
    Off by default: interactive logins from a server IP frequently trigger checkpoints.
    """
    username = os.environ.get("IG_USERNAME")
    password = os.environ.get("IG_PASSWORD")
    if not username or not password:
        return False
    try:
        logging.info(f"Attempting direct login for @{username} (from env vars)...")
        cl.login(username, password)
        if test_session_validity(cl, CONFIG["crawler"].get("test_probe_tag", "trading")):
            cl.dump_settings(settings_file)
            logging.info(f"Login successful; device session saved to {settings_file}.")
            return True
        errors.append("direct login succeeded but session probe failed (checkpoint?)")
    except Exception as e:
        errors.append(f"direct login failed: {e}")
        logging.warning(f"Direct login failed: {e}")
    return False


def get_authenticated_client() -> Client:
    settings_file = os.path.join(ROOT_DIR, CONFIG["storage"]["session_settings_file"])
    cookie_file = os.path.join(ROOT_DIR, CONFIG["storage"]["cookie_cache_file"])
    probe_tag = CONFIG["crawler"].get("test_probe_tag", "trading")

    cl = Client()
    cl.delay_range = [2, 4]

    errors = []
    if _try_settings(cl, settings_file, probe_tag, errors):
        return cl
    if _try_cookies(cl, cookie_file, settings_file, probe_tag, errors):
        return cl
    if CONFIG["crawler"].get("auto_browser_cookies", True):
        if _try_browser_cookies(cl, settings_file, probe_tag, errors):
            return cl
    if _try_password_login(cl, settings_file, errors):
        return cl

    raise RuntimeError(
        "Authentication failed. Attempts made:\n  - " + "\n  - ".join(errors) +
        "\n\nFix options:\n"
        "  1. Log into instagram.com in your browser (auto-extraction will pick it up).\n"
        "  2. Or export cookies to data/instagram_cookies.json and DELETE session_settings.json.\n"
        "  3. Or set IG_USERNAME / IG_PASSWORD env vars (may trigger checkpoint)."
    )
