# -*- coding: utf-8 -*-
"""
STEP103-08 V2 TimelineLogger

역할:
- blog_v2_pipeline_timeline 테이블에 V2 Pipeline / Stage 이벤트를 저장한다.
- Pipeline 시작/종료, Stage 시작/종료, 실패 지점을 시간순으로 기록한다.
- Resume Engine, Server 관제센터 V2, PHP 관리자 V2에서 사용한다.

원칙:
- V1 테이블은 수정하지 않는다.
- Timeline은 V2 운영 추적 전용이다.
"""

import json
from datetime import datetime


class TimelineLogger:
    table_name = "blog_v2_pipeline_timeline"

    def __init__(self, conn, scheduler_key=None):
        self.conn = conn
        self.scheduler_key = scheduler_key

    def pipeline_start(self, context, message="pipeline start", meta=None):
        return self.log_event(
            context=context,
            stage="pipeline",
            event_type="start",
            status="running",
            success=None,
            message=message,
            started_at=datetime.now(),
            meta=meta,
        )

    def pipeline_finish(self, context, success=True, message="pipeline finish", error_message=None, meta=None):
        return self.log_event(
            context=context,
            stage="pipeline",
            event_type="finish",
            status="success" if success else "failed",
            success=success,
            message=message,
            error_message=error_message,
            finished_at=datetime.now(),
            meta=meta,
        )

    def stage_start(self, context, stage, message=None, meta=None):
        return self.log_event(
            context=context,
            stage=stage,
            event_type="start",
            status="running",
            success=None,
            message=message or f"{stage} start",
            started_at=datetime.now(),
            meta=meta,
        )

    def stage_finish(
        self,
        context,
        stage,
        success=True,
        message=None,
        error_message=None,
        elapsed=None,
        meta=None,
    ):
        elapsed_ms = None
        if elapsed is not None:
            try:
                elapsed_ms = int(float(elapsed) * 1000)
            except Exception:
                elapsed_ms = None

        return self.log_event(
            context=context,
            stage=stage,
            event_type="finish",
            status="success" if success else "failed",
            success=success,
            message=message or f"{stage} finish",
            error_message=error_message,
            finished_at=datetime.now(),
            elapsed_ms=elapsed_ms,
            meta=meta,
        )

    def log_event(
        self,
        context,
        stage,
        event_type,
        status=None,
        success=None,
        message=None,
        error_message=None,
        started_at=None,
        finished_at=None,
        elapsed_ms=None,
        article_no=None,
        draft_id=None,
        publish_queue_id=None,
        meta=None,
    ):
        meta_text = json.dumps(meta or {}, ensure_ascii=False, default=str)

        scheduler_key = (
            self.scheduler_key
            or context.get("scheduler_key", None)
            or "manual_v2"
        )

        resolved_article_no = (
            article_no
            or self._from_context_target(context, "article_no")
            or context.get("article_no")
        )

        resolved_draft_id = (
            draft_id
            or self._from_context_target(context, "draft_id")
        )

        resolved_publish_queue_id = (
            publish_queue_id
            or self._from_context_target(context, "publish_queue_id")
        )

        sql = f"""
            INSERT INTO {self.table_name}
            (
                pipeline_id,
                scheduler_key,
                realtor_id,
                article_no,
                draft_id,
                publish_queue_id,
                stage,
                event_type,
                status,
                success,
                message,
                error_message,
                started_at,
                finished_at,
                elapsed_ms,
                meta_text,
                created_at
            )
            VALUES
            (
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                %s,
                NOW()
            )
        """

        params = (
            context.pipeline_id,
            scheduler_key,
            int(context.realtor_id),
            str(resolved_article_no) if resolved_article_no else None,
            self._to_int_or_none(resolved_draft_id),
            self._to_int_or_none(resolved_publish_queue_id),
            str(stage),
            str(event_type),
            status,
            None if success is None else (1 if success else 0),
            message,
            error_message,
            started_at,
            finished_at,
            elapsed_ms,
            meta_text,
        )

        with self.conn.cursor() as cur:
            cur.execute(sql, params)
            log_id = cur.lastrowid

        self.conn.commit()
        return log_id

    def _from_context_target(self, context, key):
        publish_data = context.get("publish_data", {}) or {}
        target = publish_data.get("target", {}) or {}
        return target.get(key)

    def _to_int_or_none(self, value):
        try:
            if value is None or value == "":
                return None
            return int(value)
        except Exception:
            return None
