import os
import sys
import json
import time
import glob
import logging
import threading
import random
from concurrent.futures import ThreadPoolExecutor, as_completed

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

if ROOT_DIR not in sys.path:
    sys.path.insert(0, ROOT_DIR)

from pinscrape.v2 import Pinterest
from pinscrape.database import get_pending_keywords, update_images
from pinscrape.utils import load_proxies

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    datefmt="%H:%M:%S"
)
logger = logging.getLogger(__name__)


def load_config():
    config_path = os.path.join(ROOT_DIR, "config.json")
    defaults = {
        "image_result": 26,
        "concurrency": 5,
        "max_retries": 3,
        "retry_delay": 3
    }
    if os.path.exists(config_path):
        with open(config_path, "r") as f:
            cfg = json.load(f)
        defaults.update(cfg)
    return defaults


def scrape_keyword(db_path, keyword_id, keyword, proxy_list, config):
    page_size = config["image_result"]
    max_retries = config["max_retries"]

    p = Pinterest(proxy_list=proxy_list, sleep_time=(1.5, 4.0), max_retries=3)

    for attempt in range(1, max_retries + 1):
        try:
            results = p.search(keyword, page_size=page_size)
            if results:
                images_json = json.dumps(results)
                update_images(db_path, keyword_id, images_json)
                return {"id": keyword_id, "keyword": keyword, "count": len(results), "error": None}
            else:
                logger.info(f"  No images found for '{keyword}', skipping database update (status stays 0)")
                return {"id": keyword_id, "keyword": keyword, "count": 0, "error": "no images found"}
        except Exception as e:
            logger.warning(f"  Attempt {attempt}/{max_retries} failed for '{keyword}': {e}")
            if attempt < max_retries:
                wait = random.uniform(3, 8)
                logger.info(f"  Waiting {wait:.1f}s before retry...")
                time.sleep(wait)
                p.reset_session()

    return {"id": keyword_id, "keyword": keyword, "count": 0, "error": "max retries exceeded"}


def scrape_database(db_path, proxy_list, config):
    db_name = os.path.basename(db_path)
    logger.info(f"\n{'='*50}")
    logger.info(f"Processing: {db_name}")
    logger.info(f"{'='*50}")

    pending = get_pending_keywords(db_path)
    if not pending:
        logger.info(f"No pending keywords in {db_name}")
        return

    logger.info(f"Found {len(pending)} pending keywords")
    concurrency = config["concurrency"]
    completed = 0
    lock = threading.Lock()

    def progress_callback(future):
        nonlocal completed
        result = future.result()
        with lock:
            completed += 1
            status = "OK" if not result["error"] else f"FAIL ({result['error']})"
            logger.info(f"  [{completed}/{len(pending)}] {result['keyword']} -> {result['count']} images [{status}]")

    with ThreadPoolExecutor(max_workers=concurrency) as executor:
        futures = {}
        for item in pending:
            future = executor.submit(
                scrape_keyword,
                db_path, item["id"], item["keyword"],
                proxy_list, config
            )
            futures[future] = item
            future.add_done_callback(progress_callback)

        for future in as_completed(futures):
            pass

    logger.info(f"Completed {db_name}: {completed}/{len(pending)} keywords processed")


def find_databases():
    db_dir = os.path.join(ROOT_DIR, "database")
    if not os.path.exists(db_dir):
        return []
    return sorted(glob.glob(os.path.join(db_dir, "*.sqlite")))


def main():
    config = load_config()
    logger.info(f"Config: image_result={config['image_result']}, concurrency={config['concurrency']}, "
                f"max_retries={config['max_retries']}, retry_delay={config['retry_delay']}s")

    proxy_list = load_proxies(os.path.join(ROOT_DIR, "proxies.txt"))
    logger.info(f"Loaded {len(proxy_list)} proxies")

    databases = find_databases()
    if not databases:
        logger.warning("No .sqlite files found in database/ folder")
        return

    logger.info(f"Found {len(databases)} database(s): {[os.path.basename(d) for d in databases]}")

    for db_path in databases:
        scrape_database(db_path, proxy_list, config)

    logger.info("\nAll databases processed!")


if __name__ == "__main__":
    main()
