# -*- coding: utf-8 -*-
"""
STEP103-12 V2 SchedulerLock

역할:
- V2 Scheduler가 동시에 2개 이상 실행되지 않도록 DB Lock을 잡는다.
- 기존 blog_app_locks 테이블을 사용한다.
- lock_name 예: v2_scheduler:server_default

주의:
- blog_app_locks 테이블에 id 컬럼이 없을 수 있으므로 lock_name PK 기준으로 동작한다.
"""

import uuid
from datetime import datetime, timedelta


class SchedulerLock:
    def __init__(self, conn, scheduler_key="server_default", ttl_minutes=720):
        self.conn = conn
        self.scheduler_key = scheduler_key
        self.lock_name = f"v2_scheduler:{scheduler_key}"
        self.ttl_minutes = int(ttl_minutes or 720)
        self.owner_token = f"v2-{uuid.uuid4().hex}"

    def acquire(self) -> bool:
        """
        락 획득.
        - 기존 락이 없으면 생성
        - 만료된 락이면 탈취
        - 살아있는 락이면 실패
        """
        expires_at = datetime.now() + timedelta(minutes=self.ttl_minutes)

        try:
            with self.conn.cursor() as cur:
                cur.execute(
                    """
                    INSERT INTO blog_app_locks
                    (
                        lock_name,
                        locked_at,
                        expires_at,
                        owner_token
                    )
                    VALUES
                    (
                        %s,
                        NOW(),
                        %s,
                        %s
                    )
                    ON DUPLICATE KEY UPDATE
                        owner_token = IF(expires_at < NOW(), VALUES(owner_token), owner_token),
                        locked_at = IF(expires_at < NOW(), NOW(), locked_at),
                        expires_at = IF(expires_at < NOW(), VALUES(expires_at), expires_at)
                    """,
                    (
                        self.lock_name,
                        expires_at,
                        self.owner_token,
                    ),
                )

                cur.execute(
                    """
                    SELECT owner_token
                    FROM blog_app_locks
                    WHERE lock_name=%s
                    LIMIT 1
                    """,
                    (self.lock_name,),
                )
                row = cur.fetchone()

            self.conn.commit()

            current_owner = row.get("owner_token") if isinstance(row, dict) else row[0]
            return current_owner == self.owner_token

        except Exception:
            self.conn.rollback()
            raise

    def heartbeat(self):
        expires_at = datetime.now() + timedelta(minutes=self.ttl_minutes)

        with self.conn.cursor() as cur:
            cur.execute(
                """
                UPDATE blog_app_locks
                SET
                    expires_at=%s,
                    locked_at=NOW()
                WHERE lock_name=%s
                  AND owner_token=%s
                """,
                (
                    expires_at,
                    self.lock_name,
                    self.owner_token,
                ),
            )
            affected = cur.rowcount

        self.conn.commit()
        return affected > 0

    def release(self):
        with self.conn.cursor() as cur:
            cur.execute(
                """
                DELETE FROM blog_app_locks
                WHERE lock_name=%s
                  AND owner_token=%s
                """,
                (
                    self.lock_name,
                    self.owner_token,
                ),
            )
            affected = cur.rowcount

        self.conn.commit()
        return affected > 0

    def current(self):
        with self.conn.cursor() as cur:
            cur.execute(
                """
                SELECT *
                FROM blog_app_locks
                WHERE lock_name=%s
                LIMIT 1
                """,
                (self.lock_name,),
            )
            row = cur.fetchone()

        return dict(row) if row else None
