# app/src/trading/journal/service.py
 
from __future__ import annotations

from datetime import datetime
from typing import Any
from zoneinfo import ZoneInfo

from src.core.config import load_settings
from src.storage.repositories.journal import JournalRepository
from src.storage.session import check_database_health
from src.trading.journal.exporter import build_csv, build_xlsx
from src.trading.journal.filters import (
    journal_export_filter_label,
    normalize_journal_export_filter,
)


EXPORT_LIMIT = 10000


class JournalService:
    def __init__(self) -> None:
        self.repository = JournalRepository()

    def _account_mode(self) -> str:
        settings = load_settings()
        return "demo" if "demo" in settings.exchange_base_url.lower() else "live"

    def _account_prefix(self) -> str:
        return f"[{self._account_mode().upper()}]"

    def _build_message(self, message: str) -> str:
        return f"{self._account_prefix()} {message}".strip()

    def _build_payload(
        self,
        *,
        user_id: int | None = None,
        chat_id: int | None = None,
        screen: str | None = None,
        action: str | None = None,
        error_type: str | None = None,
        raw_error: str | None = None,
        payload: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        result: dict[str, Any] = dict(payload or {})
        result.setdefault("account_mode", self._account_mode())

        if user_id is not None:
            result.setdefault("user_id", user_id)

        if chat_id is not None:
            result.setdefault("chat_id", chat_id)

        if screen is not None:
            result.setdefault("screen", screen)

        if action is not None:
            result.setdefault("action", action)

        if error_type is not None:
            result.setdefault("error_type", error_type)

        if raw_error is not None:
            result.setdefault("raw_error", raw_error)

        return result

    def log_info(
        self,
        event_type: str,
        message: str,
        payload: dict[str, Any] | None = None,
    ) -> None:
        self.repository.add_event(
            level="INFO",
            event_type=event_type,
            message=message,
            payload=payload,
        )

    def log_warning(
        self,
        event_type: str,
        message: str,
        payload: dict[str, Any] | None = None,
    ) -> None:
        self.repository.add_event(
            level="WARNING",
            event_type=event_type,
            message=message,
            payload=payload,
        )

    def log_error(
        self,
        event_type: str,
        message: str,
        payload: dict[str, Any] | None = None,
    ) -> None:
        self.repository.add_event(
            level="ERROR",
            event_type=event_type,
            message=message,
            payload=payload,
        )

    def log_critical(
        self,
        event_type: str,
        message: str,
        payload: dict[str, Any] | None = None,
    ) -> None:
        self.repository.add_event(
            level="CRITICAL",
            event_type=event_type,
            message=message,
            payload=payload,
        )

    def log_debug(
        self,
        event_type: str,
        message: str,
        payload: dict[str, Any] | None = None,
    ) -> None:
        if not load_settings().journal_debug_enabled:
            return

        self.log_info(
            event_type=event_type,
            message=f"[DEBUG] {message}",
            payload=payload,
        )

    def log_ui_debug(
        self,
        *,
        event_type: str,
        message: str,
        screen: str,
        action: str,
        user_id: int | None = None,
        chat_id: int | None = None,
        payload: dict[str, Any] | None = None,
    ) -> None:
        if not load_settings().journal_debug_enabled:
            return

        self.log_info(
            event_type=event_type,
            message=self._build_message(f"[DEBUG] {message}"),
            payload=self._build_payload(
                user_id=user_id,
                chat_id=chat_id,
                screen=screen,
                action=action,
                payload=payload,
            ),
        )

    def log_ui_info(
        self,
        *,
        event_type: str,
        message: str,
        screen: str,
        action: str,
        user_id: int | None = None,
        chat_id: int | None = None,
        payload: dict[str, Any] | None = None,
    ) -> None:
        self.log_info(
            event_type=event_type,
            message=self._build_message(message),
            payload=self._build_payload(
                user_id=user_id,
                chat_id=chat_id,
                screen=screen,
                action=action,
                payload=payload,
            ),
        )

    def log_ui_warning(
        self,
        *,
        event_type: str,
        message: str,
        screen: str,
        action: str,
        user_id: int | None = None,
        chat_id: int | None = None,
        payload: dict[str, Any] | None = None,
        error_type: str | None = None,
        raw_error: str | None = None,
    ) -> None:
        self.log_warning(
            event_type=event_type,
            message=self._build_message(message),
            payload=self._build_payload(
                user_id=user_id,
                chat_id=chat_id,
                screen=screen,
                action=action,
                error_type=error_type,
                raw_error=raw_error,
                payload=payload,
            ),
        )

    def log_ui_error(
        self,
        *,
        event_type: str,
        message: str,
        screen: str,
        action: str,
        user_id: int | None = None,
        chat_id: int | None = None,
        payload: dict[str, Any] | None = None,
        error_type: str | None = None,
        raw_error: str | None = None,
    ) -> None:
        self.log_error(
            event_type=event_type,
            message=self._build_message(message),
            payload=self._build_payload(
                user_id=user_id,
                chat_id=chat_id,
                screen=screen,
                action=action,
                error_type=error_type,
                raw_error=raw_error,
                payload=payload,
            ),
        )

    def get_recent(self, limit: int = 10) -> list[dict[str, Any]]:
        return self.repository.list_recent_events(limit=limit)

    def get_page(self, page: int = 1, page_size: int = 5) -> list[dict[str, Any]]:
        offset = (page - 1) * page_size
        return self.repository.list_recent_with_offset(limit=page_size, offset=offset)

    def get_total_count(self) -> int:
        return self.repository.count_events()

    def get_export_rows(
        self,
        limit: int = EXPORT_LIMIT,
        export_filter: str = "all",
    ) -> list[dict[str, Any]]:
        filter_key = normalize_journal_export_filter(export_filter)

        return self.repository.list_export_rows(
            limit=limit,
            export_filter=filter_key,
        )

    def _journal_level(self) -> str:
        return "INFO+"

    def _export_timestamp(self) -> str:
        settings = load_settings()
        try:
            now = datetime.now(ZoneInfo(settings.tz))
        except Exception:
            now = datetime.utcnow()

        return now.strftime("%Y-%m-%d_%H-%M-%S")

    def build_export_filename(
        self,
        extension: str,
        export_filter: str = "all",
    ) -> str:
        safe_extension = extension.lower().strip().lstrip(".")
        safe_level = self._journal_level().lower().replace("+", "_plus")
        safe_filter = normalize_journal_export_filter(export_filter)

        return (
            f"journal_"
            f"{self._account_mode()}_"
            f"{safe_filter}_"
            f"{safe_level}_"
            f"{self._export_timestamp()}."
            f"{safe_extension}"
        )

    def export_csv(
        self,
        limit: int = EXPORT_LIMIT,
        export_filter: str = "all",
    ) -> bytes:
        filter_key = normalize_journal_export_filter(export_filter)
        rows = self.get_export_rows(
            limit=limit,
            export_filter=filter_key,
        )

        return build_csv(
            rows,
            total_count=self.get_total_count(),
            export_limit=limit,
            account_mode=self._account_mode(),
            journal_level=self._journal_level(),
            export_filter_label=journal_export_filter_label(filter_key),
        )

    def export_xlsx(
        self,
        limit: int = EXPORT_LIMIT,
        export_filter: str = "all",
    ) -> bytes:
        filter_key = normalize_journal_export_filter(export_filter)
        rows = self.get_export_rows(
            limit=limit,
            export_filter=filter_key,
        )

        return build_xlsx(
            rows,
            total_count=self.get_total_count(),
            export_limit=limit,
            account_mode=self._account_mode(),
            journal_level=self._journal_level(),
            export_filter_label=journal_export_filter_label(filter_key),
        )

    def clear_all(self) -> int:
        deleted_count = self.repository.delete_all()

        self.log_ui_warning(
            event_type="journal_cleared",
            message="Журнал очищен.",
            screen="journal",
            action="clear",
            payload={"deleted_count": deleted_count},
        )

        return deleted_count

    def get_journal_health(self) -> tuple[bool, str]:
        db_ok, db_message = check_database_health()
        if not db_ok:
            return False, f"Журнал недоступен: {db_message}"

        try:
            total = self.repository.count_events()
        except Exception as exc:
            return False, f"Ошибка чтения журнала: {exc}"

        return True, f"Журнал работает. Событий: {total}"

    def clear_older_than_days(self, days: int) -> int:
        deleted_count = self.repository.delete_older_than_days(days)

        self.log_ui_warning(
            event_type="journal_cleared",
            message=f"Журнал очищен: удалены записи старше {days} дней.",
            screen="journal",
            action="clear_old",
            payload={
                "days": days,
                "deleted_count": deleted_count,
            },
        )

        return deleted_count
    
    def _build_trade_payload(
        self,
        *,
        state: object,
        action: str,
        trade_id: str | None = None,
        extra: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        # Единый payload сделки для будущего анализа стратегии.
        # Порядок блоков важен: так экспорт журнала легче читать и сравнивать.
        payload: dict[str, Any] = {
            # ---------- Trade identity ----------
            "trade_id": trade_id,
            "action": action,
            "symbol": getattr(state, "symbol", None),
            "strategy": getattr(state, "strategy", None),
            "status": getattr(state, "status", None),
            "cycle_number": getattr(state, "cycle_number", None),

            # ---------- Position at event moment ----------
            "position_side": getattr(state, "position_side", None),
            "entry_price": getattr(state, "entry_price", None),
            "position_size": getattr(state, "position_size", None),
            "leverage": getattr(state, "leverage", None),

            # ---------- PnL / cycle statistics ----------
            "unrealized_pnl_usd": getattr(state, "unrealized_pnl_usd", None),
            "realized_pnl_usd": getattr(state, "realized_pnl_usd", None),
            "cycle_realized_pnl_usd": getattr(state, "cycle_realized_pnl_usd", None),
            "cycle_closed_trades": getattr(state, "cycle_closed_trades", None),
            "cycle_winning_trades": getattr(state, "cycle_winning_trades", None),
            "cycle_losing_trades": getattr(state, "cycle_losing_trades", None),
            "cycle_consecutive_losses": getattr(state, "cycle_consecutive_losses", None),

            # ---------- Signal / decision ----------
            "last_signal": getattr(state, "last_signal", None),
            "last_signal_confidence": getattr(state, "last_signal_confidence", None),
            "last_signal_reason": getattr(state, "last_signal_reason", None),
            "decision_status": getattr(state, "decision_status", None),
            "decision_reason": getattr(state, "decision_reason", None),

            # ---------- Market summary ----------
            "market_state": getattr(state, "market_state", None),
            "market_trend": getattr(state, "market_trend", None),
            "market_trend_strength": getattr(state, "market_trend_strength", None),
            "market_trend_quality": getattr(state, "market_trend_quality", None),
            "market_phase": getattr(state, "market_phase", None),
            "market_phase_direction": getattr(state, "market_phase_direction", None),

            # ---------- Market score ----------
            "market_score": getattr(state, "market_score", None),
            "market_score_label": getattr(state, "market_score_label", None),
            "market_long_score": getattr(state, "market_long_score", None),
            "market_short_score": getattr(state, "market_short_score", None),

            # ---------- Candle / interval context ----------
            "last_closed_candle_change_percent": getattr(state, "last_closed_candle_change_percent", None),
            "last_closed_candle_direction": getattr(state, "last_closed_candle_direction", None),
            "current_interval_change_percent": getattr(state, "current_interval_change_percent", None),
            "current_interval_direction": getattr(state, "current_interval_direction", None),
            "current_interval_label": getattr(state, "current_interval_label", None),

            # ---------- Market structure ----------
            "market_structure": getattr(state, "market_structure", None),
            "market_structure_reason": getattr(state, "market_structure_reason", None),

            # ---------- Momentum / breakout ----------
            "momentum_state": getattr(state, "momentum_state", None),
            "momentum_direction": getattr(state, "momentum_direction", None),
            "momentum_strength": getattr(state, "momentum_strength", None),
            "momentum_change_percent": getattr(state, "momentum_change_percent", None),

            # ---------- Execution quality ----------
            "execution_quality": getattr(state, "execution_quality", None),
            "execution_quality_reason": getattr(state, "execution_quality_reason", None),
            "execution_confidence_score": getattr(state, "execution_confidence_score", None),
            "execution_confidence_level": getattr(state, "execution_confidence_level", None),
            "spread_percent": getattr(state, "spread_percent", None),
            "snapshot_age_seconds": getattr(state, "snapshot_age_seconds", None),

            # ---------- Adaptive size ----------
            "adaptive_size_base": getattr(state, "adaptive_size_base", None),
            "adaptive_size_final": getattr(state, "adaptive_size_final", None),
            "adaptive_size_multiplier": getattr(state, "adaptive_size_multiplier", None),
            "adaptive_size_reason": getattr(state, "adaptive_size_reason", None),

            # ---------- Position analytics ----------
            "position_mfe_percent": getattr(state, "position_mfe_percent", None),
            "position_mae_percent": getattr(state, "position_mae_percent", None),
            "position_peak_pnl_usd": getattr(state, "position_peak_pnl_usd", None),
            "position_hold_seconds": getattr(state, "position_hold_seconds", None),
        }

        if extra:
            payload.update(extra)

        return payload

    def log_trade_event(
        self,
        *,
        event_type: str,
        message: str,
        state: object,
        action: str,
        trade_id: str | None = None,
        payload: dict[str, Any] | None = None,
    ) -> None:
        # Trade-события пишем в общий журнал, чтобы экспорт CSV/XLSX уже работал без новой таблицы.
        self.log_info(
            event_type=event_type,
            message=self._build_message(message),
            payload=self._build_payload(
                screen="auto",
                action=action,
                payload=self._build_trade_payload(
                    state=state,
                    action=action,
                    trade_id=trade_id,
                    extra=payload,
                ),
            ),
        )