# -*- coding: utf-8 -*-
"""
STEP104-04 BehaviorLogger with DB

역할:
- HPE 발행 행동을 기록한다.
- conn이 있으면 blog_v2_publish_behavior 테이블에 저장한다.
- conn이 없으면 기존처럼 콘솔/메모리 기록만 수행한다.
"""

import json


class BehaviorLogger:
    def __init__(self, conn=None, verbose=True):
        self.conn = conn
        self.verbose = verbose
        self.records = []
        self._running_ids = {}
        self._order = 0

    def start_behavior(self, session, behavior_name, meta=None, parent_behavior=None):
        self._order += 1

        record = {
            "event": "start",
            "session_id": session.session_id,
            "pipeline_id": session.pipeline_id,
            "realtor_id": session.realtor_id,
            "publish_queue_id": session.publish_queue_id,
            "article_no": session.article_no,
            "draft_id": session.draft_id,
            "actor_name": session.actor_name,
            "profile_name": session.profile_name,
            "behavior_name": behavior_name,
            "behavior_order": self._order,
            "parent_behavior": parent_behavior,
            "status": "running",
            "meta": meta or {},
        }
        self.records.append(record)

        db_id = None
        if self.conn:
            db_id = self.insert_running(session, behavior_name, record)

        self._running_ids[self._key(session, behavior_name)] = db_id

        if self.verbose:
            print("[HPE BEHAVIOR START]", f"session={session.session_id}", f"behavior={behavior_name}")

        return record

    def finish_behavior(self, session, behavior_name, success=True, elapsed_ms=None, error_message=None, meta=None):
        status = "success" if success else "failed"

        record = {
            "event": "finish",
            "session_id": session.session_id,
            "pipeline_id": session.pipeline_id,
            "realtor_id": session.realtor_id,
            "publish_queue_id": session.publish_queue_id,
            "article_no": session.article_no,
            "draft_id": session.draft_id,
            "actor_name": session.actor_name,
            "profile_name": session.profile_name,
            "behavior_name": behavior_name,
            "status": status,
            "success": bool(success),
            "elapsed_ms": elapsed_ms,
            "error_message": error_message,
            "meta": meta or {},
        }
        self.records.append(record)

        if self.conn:
            db_id = self._running_ids.get(self._key(session, behavior_name))
            if db_id:
                self.update_finished(db_id, status, elapsed_ms, error_message, meta)
            else:
                self.insert_finished(session, behavior_name, record)

        if self.verbose:
            print(
                "[HPE BEHAVIOR FINISH]",
                f"session={session.session_id}",
                f"behavior={behavior_name}",
                f"success={success}",
                f"elapsed_ms={elapsed_ms}",
            )

        return record

    def insert_running(self, session, behavior_name, record):
        sql = """
            INSERT INTO blog_v2_publish_behavior
            (
                session_id, pipeline_id, publish_queue_id, realtor_id,
                article_no, draft_id, actor_name, profile_name,
                behavior_name, behavior_order, parent_behavior,
                status, started_at, meta_json, created_at
            )
            VALUES
            (
                %s, %s, %s, %s,
                %s, %s, %s, %s,
                %s, %s, %s,
                'running', NOW(), %s, NOW()
            )
        """
        params = (
            session.session_id,
            session.pipeline_id,
            self._to_int_or_none(session.publish_queue_id),
            self._to_int_or_none(session.realtor_id),
            str(session.article_no) if session.article_no else None,
            self._to_int_or_none(session.draft_id),
            session.actor_name,
            session.profile_name,
            behavior_name,
            record.get("behavior_order"),
            record.get("parent_behavior"),
            json.dumps(record.get("meta") or {}, ensure_ascii=False, default=str),
        )

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

        self.conn.commit()
        return db_id

    def update_finished(self, db_id, status, elapsed_ms=None, error_message=None, meta=None):
        sql = """
            UPDATE blog_v2_publish_behavior
            SET
                status=%s,
                finished_at=NOW(),
                elapsed_ms=%s,
                error_message=%s,
                meta_json=%s
            WHERE id=%s
        """

        with self.conn.cursor() as cur:
            cur.execute(
                sql,
                (
                    status,
                    self._to_int_or_none(elapsed_ms),
                    str(error_message)[:5000] if error_message else None,
                    json.dumps(meta or {}, ensure_ascii=False, default=str),
                    int(db_id),
                ),
            )

        self.conn.commit()

    def insert_finished(self, session, behavior_name, record):
        sql = """
            INSERT INTO blog_v2_publish_behavior
            (
                session_id, pipeline_id, publish_queue_id, realtor_id,
                article_no, draft_id, actor_name, profile_name,
                behavior_name, status, finished_at, elapsed_ms,
                error_message, meta_json, created_at
            )
            VALUES
            (
                %s, %s, %s, %s,
                %s, %s, %s, %s,
                %s, %s, NOW(), %s,
                %s, %s, NOW()
            )
        """

        with self.conn.cursor() as cur:
            cur.execute(
                sql,
                (
                    session.session_id,
                    session.pipeline_id,
                    self._to_int_or_none(session.publish_queue_id),
                    self._to_int_or_none(session.realtor_id),
                    str(session.article_no) if session.article_no else None,
                    self._to_int_or_none(session.draft_id),
                    session.actor_name,
                    session.profile_name,
                    behavior_name,
                    record.get("status"),
                    self._to_int_or_none(record.get("elapsed_ms")),
                    str(record.get("error_message"))[:5000] if record.get("error_message") else None,
                    json.dumps(record.get("meta") or {}, ensure_ascii=False, default=str),
                ),
            )
            db_id = cur.lastrowid

        self.conn.commit()
        return db_id

    def _key(self, session, behavior_name):
        return f"{session.session_id}:{behavior_name}"

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