#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
快取單一來源模組。

ADR-017 Phase 3f-2: 將 sales/import/export/daily 會共同碰到的
module-level cache 收斂到這裡，避免各 route 各自持有一份 dict。
"""

import os
import logging
import time
from pathlib import Path


logger = logging.getLogger(__name__)


class FingerprintCache:
    """TTL + fingerprint 的小型 in-memory cache。"""

    def __init__(self, name, fingerprint_fn):
        self.name = name
        self._store = {}
        self._fp_fn = fingerprint_fn

    def get(self, key, ttl=300):
        entry = self._store.get(key)
        if not entry:
            return None
        if time.time() - entry['ts'] > ttl:
            return None
        try:
            if self._fp_fn() != entry['fp']:
                return None
        except Exception:
            logger.debug("cache fingerprint check failed for %s", self.name, exc_info=True)
        return entry['data']

    def set(self, key, data):
        try:
            fp = self._fp_fn()
        except Exception:
            logger.debug("cache fingerprint write failed for %s", self.name, exc_info=True)
            fp = None
        self._store[key] = {'data': data, 'ts': time.time(), 'fp': fp}

    def clear(self):
        self._store.clear()


_SALES_DF_CACHE = {}
_SALES_PROCESSED_CACHE = {}
_SALES_OPTIONS_CACHE = {}
_SALES_ANALYSIS_RESULT_CACHE = {}
_SALES_CACHE_MAX_ENTRIES = 10
_SALES_CACHE_TTL = 600

_DAILY_SALES_PROCESSED_CACHE = {}

_DASHBOARD_DATA_CACHE = {
    'consolidated_data': None,
    'consolidated_timestamp': None,
    'today_start': None,
    'full_data': None,
    'full_timestamp': None,
}
_DASHBOARD_CACHE_TTL = 1800
_BASE_DIR = Path(__file__).resolve().parents[1]
_DASHBOARD_SHARED_CACHE_FILE = _BASE_DIR / "data" / "dashboard_full_cache.pkl"
_DASHBOARD_STALE_CACHE_FILE = _BASE_DIR / "data" / "dashboard_full_cache_stale.pkl"
_SALES_ANALYSIS_PAGE_CACHE_DIR = _BASE_DIR / "data" / "sales_analysis_page_cache"
_DAILY_SALES_VIEW_CACHE_DIR = _BASE_DIR / "data" / "daily_sales_view_cache"


def cleanup_sales_cache():
    """清理 sales 處理後快取的過期與超額條目。"""
    current_time = time.time()
    expired_keys = [
        key for key, value in _SALES_PROCESSED_CACHE.items()
        if value.get('time') and current_time - value['time'] > _SALES_CACHE_TTL
    ]
    for key in expired_keys:
        _SALES_PROCESSED_CACHE.pop(key, None)

    if len(_SALES_PROCESSED_CACHE) > _SALES_CACHE_MAX_ENTRIES:
        sorted_items = sorted(
            [(key, value.get('time', 0)) for key, value in _SALES_PROCESSED_CACHE.items()],
            key=lambda item: item[1],
        )
        for key, _ in sorted_items[:-_SALES_CACHE_MAX_ENTRIES]:
            _SALES_PROCESSED_CACHE.pop(key, None)


def set_sales_processed_cache(key, entry, aliases=()):
    """寫入 sales cache，並可同步寫入別名 key。"""
    entry.setdefault('time', time.time())
    _SALES_PROCESSED_CACHE[key] = entry
    for alias in aliases:
        _SALES_PROCESSED_CACHE[alias] = entry
    cleanup_sales_cache()


def clear_sales_cache_for_table(table_name):
    """匯入資料後清除指定表對應的所有 sales cache key。"""
    _SALES_DF_CACHE.pop(table_name, None)
    keys_to_delete = [
        key for key in list(_SALES_PROCESSED_CACHE.keys())
        if key == table_name or key.startswith(f"{table_name}_")
    ]
    for key in keys_to_delete:
        _SALES_PROCESSED_CACHE.pop(key, None)


def clear_sales_cache():
    """清除所有 sales cache。"""
    _SALES_DF_CACHE.clear()
    _SALES_PROCESSED_CACHE.clear()
    _SALES_OPTIONS_CACHE.clear()
    _SALES_ANALYSIS_RESULT_CACHE.clear()
    try:
        if _SALES_ANALYSIS_PAGE_CACHE_DIR.exists():
            for cache_file in _SALES_ANALYSIS_PAGE_CACHE_DIR.glob("*.pkl"):
                try:
                    cache_file.unlink()
                except OSError:
                    logger.debug("sales analysis page cache file cleanup failed: %s", cache_file, exc_info=True)
    except OSError:
        logger.debug("sales analysis page cache cleanup failed", exc_info=True)


def clear_daily_sales_cache():
    """清除當日業績 cache。"""
    _DAILY_SALES_PROCESSED_CACHE.clear()
    try:
        if _DAILY_SALES_VIEW_CACHE_DIR.exists():
            for cache_file in _DAILY_SALES_VIEW_CACHE_DIR.glob("*.pkl"):
                try:
                    cache_file.unlink()
                except OSError:
                    logger.debug("daily sales view cache file cleanup failed: %s", cache_file, exc_info=True)
    except OSError:
        logger.debug("daily sales view cache cleanup failed", exc_info=True)


def clear_dashboard_cache():
    """清除商品看板 cache。"""
    _DASHBOARD_DATA_CACHE.clear()
    _DASHBOARD_DATA_CACHE.update({
        'consolidated_data': None,
        'consolidated_timestamp': None,
        'today_start': None,
        'full_data': None,
        'full_timestamp': None,
    })
    try:
        if os.path.exists(_DASHBOARD_SHARED_CACHE_FILE):
            try:
                os.replace(_DASHBOARD_SHARED_CACHE_FILE, _DASHBOARD_STALE_CACHE_FILE)
            except OSError:
                os.remove(_DASHBOARD_SHARED_CACHE_FILE)
    except FileNotFoundError:
        pass
    except OSError:
        logger.debug("dashboard shared cache cleanup failed", exc_info=True)
