import datetime
import math
import os

import cv2
import numpy as np
from PIL import Image

from utils import arrange_hand

# from transformers import pipeline


# # --- グローバル変数としてTrOCRパイプラインを初期化 ---
# print("TrOCRのAIモデルを読み込んでいます...(初回は数分かかります)")
# try:
#     trocr_pipeline = pipeline(
#         "image-to-text", model="microsoft/trocr-base-printed"
#     )
#     print("TrOCRの準備が完了しました。")
# except Exception as e:
#     print(f"TrOCRモデルのロード中にエラーが発生しました: {e}")
#     trocr_pipeline = None

generate_kwargs_sampling = {
    "do_sample": True,
    "temperature": 0.7,
    "top_k": 50,
    "max_length": 2,
}

SUIT_TEMPLATE_PATH = "templates/suits/"
SUITS_BY_COLOR = {"black": "S", "green": "C", "red": "H", "orange": "D"}
SCALE_STANDARD = 2032

IS_DEBUG = False


def load_suit_templates(template_path):
    templates = {}
    if not os.path.exists(template_path):
        return templates
    for filename in os.listdir(template_path):
        if filename.endswith(".png"):
            name = os.path.splitext(filename)[0]
            img = cv2.imread(
                os.path.join(template_path, filename), cv2.IMREAD_GRAYSCALE
            )
            if img is not None:
                templates[name] = img
    return templates


def get_img_with_rect(img, rects, color, thickness):
    _img = img.copy()
    for rect in rects:
        if isinstance(rect, tuple):
            x, y, w, h = rect
        else:
            (x, y), (w, h) = rect["pos"], rect["size"]
        cv2.rectangle(_img, (x, y), (x + w, y + h), color)

    return _img


def save_img_with_rect(filename, img, rects, color=(0, 255, 0), thickness=2):
    if IS_DEBUG:
        cv2.imwrite(filename, get_img_with_rect(img, rects, color, thickness))


def get_masks(hsv):
    board_candidates = [
        ((15, 200, 160), (35, 255, 245)),  # yellow
        ((100, 0, 0), (179, 60, 80)),  # black
        ((35, 200, 100), (50, 255, 160)),  # light green
        ((170, 170, 150), (179, 255, 220)),  # red
        ((160, 70, 170), (180, 120, 240)),  # pink
        ((0, 200, 160), (15, 255, 240)),  # orange
        ((90, 110, 160), (120, 210, 240)),  # blue
    ]
    for color in board_candidates:
        yield cv2.inRange(hsv, color[0], color[1])


def find_best_contour(image, is_best):
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    i = 0
    for mask in get_masks(hsv):
        kernel = np.ones((7, 7), np.uint8)
        closed_mask = cv2.morphologyEx(
            mask, cv2.MORPH_CLOSE, kernel, iterations=2
        )

        if IS_DEBUG:
            cv2.imwrite(f"debug_mask{i}.jpg", closed_mask)
        contours, _ = cv2.findContours(
            closed_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
        )
        best_contour = max(contours, key=cv2.contourArea) if contours else None
        if best_contour is not None and cv2.contourArea(best_contour) > 50000:
            b, res = is_best(best_contour)
            if b:
                return res
        i += 1
    return None


def find_board_corners(image):
    """
    画像から黄色いボードの輪郭を見つけ、その4つの角の座標を返す。
    """

    def is_best(best_contour):
        peri = cv2.arcLength(best_contour, True)
        approx = cv2.approxPolyDP(best_contour, 0.02 * peri, True)

        # 輪郭が4つの角を持つ場合、それを返す
        is_rect = len(approx) >= 4
        return is_rect, approx.reshape(-1, 2) if is_rect else None

    points = find_best_contour(image, is_best)
    # if not points:
    #     return None
    sum = points.sum(axis=1)
    diff = np.diff(points, axis=1)

    top_left = points[np.argmin(sum)]
    bottom_right = points[np.argmax(sum)]
    top_right = points[np.argmax(diff)]
    bottom_left = points[np.argmin(diff)]

    corners = np.array(
        [top_left, top_right, bottom_right, bottom_left], dtype="int32"
    )

    return corners


def order_points(pts):
    """
    4つの点を左上、右上、右下、左下の順に並べ替える。
    """
    rect = np.zeros((4, 2), dtype="float32")
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]  # 左上
    rect[2] = pts[np.argmax(s)]  # 右下

    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]  # 右上
    rect[3] = pts[np.argmax(diff)]  # 左下
    return rect


def find_center_box(image):
    def is_best(best_contour):
        if best_contour is not None and cv2.contourArea(best_contour) > 50000:
            print(f"rect:{cv2.boundingRect(best_contour)}")
            x, y, w, h = cv2.boundingRect(best_contour)
            h_parent, w_parent, _ = image.shape
            if (w < h and (w > 0.34 * w_parent or h > 0.8 * h_parent)) or (
                w >= h and (h > 0.34 * h_parent or w > 0.8 * w_parent)
            ):
                return False, None
            return True, cv2.boundingRect(best_contour)
        return False, None

    return find_best_contour(image, is_best)


def find_center_seal(image, bw, bh):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    mask = cv2.inRange(gray, 190, 255)
    kernel = np.ones((7, 7), np.uint8)
    closed_mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=2)

    if IS_DEBUG:
        cv2.imwrite(f"debug_seal.jpg", closed_mask)
    contours, _ = cv2.findContours(
        closed_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
    )
    for contour in contours:
        x, y, w, h = cv2.boundingRect(contour)
        _w = min(w, h)
        _h = max(w, h)
        if (
            _w > 0.33 * bw
            and _w < 0.44 * bw
            and _h > 0.21 * bh
            and _h < 0.24 * bh
        ):
            return x, y
    return -1, -1


def rotate_rect(box, w_parent, h_parent, angle):
    x, y, w, h = box
    if angle % 360 == 0:
        return (x, y, w, h)
    elif angle % 360 == 90:
        return (h_parent - h - y, x, h, w)
    elif angle % 360 == 180:
        return (w_parent - w - x, h_parent - h - y, w, h)
    elif angle % 360 == 270:
        return (y, w_parent - w - x, h, w)


def determine_and_correct_orientation(image, progress_fn):
    """
    画像の向きを判断し、必要であれば回転させて補正した画像を返す。
    """
    progress_fn("写真の向きを自動分析中...")

    # ★★★ ステップ1: ボードの4つの角を検出し、射影変換を行う ★★★
    print("ボードの傾きを検出・補正しています...")
    corners = find_board_corners(image)
    print(corners)
    if corners is None:
        print("エラー: ボードの角を検出できませんでした。")
        warped_image = image
    else:
        # 4つの角を正しい順序に並べ替える
        ordered_corners = order_points(corners.astype(np.float32))
        (tl, tr, br, bl) = ordered_corners

        # 変換後の画像の幅と高さを計算
        widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
        widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
        boardWidth = max(int(widthA), int(widthB))

        heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
        heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
        boardHeight = max(int(heightA), int(heightB))

        imageHeight, imageWidth, _ = image.shape
        CanvasWidth, CanvasHeight = int(imageWidth * 1.2), int(
            imageHeight * 1.2
        )

        x_offset = (CanvasWidth - boardWidth) // 2
        y_offset = (CanvasHeight - boardHeight) // 2

        # 変換後の座標を定義
        dst_pts = np.array(
            [
                [x_offset, y_offset],
                [x_offset + boardWidth - 1, y_offset],
                [x_offset + boardWidth - 1, y_offset + boardHeight - 1],
                [x_offset, y_offset + boardHeight - 1],
            ],
            dtype="float32",
        )
        print(dst_pts)

        # 射影変換行列を取得し、画像を補正
        matrix = cv2.getPerspectiveTransform(ordered_corners, dst_pts)
        warped_image = cv2.warpPerspective(
            image, matrix, (CanvasWidth, CanvasHeight)
        )

    # デバッグ用に補正後画像を保存
    cv2.imwrite("debug_warped_image.jpg", warped_image)
    print("傾き補正後の画像を debug_warped_image.jpg に保存しました。")

    # まず中央のボードを見つける
    box = find_center_box(warped_image)
    if box is None:
        progress_fn(
            "警告: ボードが見つからないため、向きの自動補正をスキップします。"
        )
        return warped_image, box, 1
    print("box is found")

    bx, by, bw, bh = box
    h, w, _ = warped_image.shape
    scale = max(bw, bh) / SCALE_STANDARD
    board_img = warped_image[by : by + bh, bx : bx + bw]
    print(box)

    image_rotated = warped_image.copy()
    if bh < bw:
        board_img = cv2.rotate(board_img, cv2.ROTATE_90_CLOCKWISE)
        image_rotated = cv2.rotate(warped_image, cv2.ROTATE_90_CLOCKWISE)
        box = rotate_rect(box, w, h, 90)
        bx, by, bw, bh = box
        h, w, _ = image_rotated.shape
    _, sy = find_center_seal(board_img, bw, bh)
    if sy == -1:
        progress_fn("ボードのシールが検出できませんでした")
        return image_rotated, box, scale
    print(sy, bx, by, bw, bh)
    if sy < bh / 2:
        return image_rotated, box, scale
    else:
        return (
            cv2.rotate(image_rotated, cv2.ROTATE_180),
            rotate_rect(box, w, h, 180),
            scale,
        )


def get_not_white_mask(img):

    lab_patch = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
    l_channel = lab_patch[:, :, 0]
    a_channel = lab_patch[:, :, 1]
    b_channel = lab_patch[:, :, 2]
    mask_l = cv2.threshold(l_channel, 170, 255, cv2.THRESH_BINARY_INV)[1]
    mask_a = cv2.threshold(a_channel, 130, 255, cv2.THRESH_BINARY)[1]
    mask_b = cv2.threshold(b_channel, 130, 255, cv2.THRESH_BINARY)[1]
    mask_ab = cv2.bitwise_or(mask_a, mask_b)
    text_mask = cv2.bitwise_and(mask_l, mask_ab)
    return text_mask


# ★★★ 新しいルールベースの色判定関数(診断モード付き) ★★★
def get_suit_from_image_rules(rank_image_patch, thresholds):
    if rank_image_patch is None or rank_image_patch.size == 0:
        return "unknown"

    text_mask = get_not_white_mask(rank_image_patch)

    # マスクを使って元のカラー画像から文字部分のみを抽出
    masked_char_image = cv2.bitwise_and(
        rank_image_patch, rank_image_patch, mask=text_mask
    )

    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f")
    if IS_DEBUG:
        # デバッグ用のフォルダがなければ作成
        debug_dir = "debug_chars"
        if not os.path.exists(debug_dir):
            os.makedirs(debug_dir)
        # ユニークなファイル名を生成
        debug_filename = os.path.join(
            debug_dir, f"masked_char_{timestamp}.png"
        )

        # 画像を保存
        cv2.imwrite(debug_filename, masked_char_image)

    lab_patch = cv2.cvtColor(masked_char_image, cv2.COLOR_BGR2LAB)
    avg_lab = cv2.mean(lab_patch, mask=text_mask)
    if cv2.countNonZero(text_mask) < 20:
        print(f"    診断: 文字ピクセルが少なすぎるため判定不可    {timestamp}")
        return "unknown", avg_lab
    return get_suit_from_color_rules(avg_lab, thresholds, timestamp)


def get_suit_from_color_rules(avg_lab, thresholds, timestamp=0):
    L, a, b = avg_lab[0], avg_lab[1], avg_lab[2]

    # --- 診断ログを出力 ---
    print(f"    L: {L:.1f}, a: {a:.1f}, b: {b:.1f}     {timestamp}")

    # ルールに基づいて判定
    # ルール1: 明るさ(L)で黒を判定
    if L < thresholds["L_black"]:
        print("    ルール1: 明るさ(L)が低いため 'black' と判定")
        return "black", avg_lab

    # ルール2: a値で緑か赤系かを判断
    # a < 128 が緑側, a > 128 が赤側
    if a < thresholds["a_green"]:  # 緑側の閾値
        # if a > thresholds["a_black"] and b > thresholds["b_black"]:
        # if a > thresholds["a_black"] and b < thresholds["b_black"]:
        #     print("    ルール6: 緑っぽいけど 'black' と判定")
        #     return "black", avg_lab
        # elif a > thresholds["a_black2"] and b < thresholds["b_black2"]:
        #     print("    ルール8: 緑っぽいけど 'black' と判定2")
        #     return "black", avg_lab
        if a + b > thresholds["ab_black"]:
            print("    ルール9: a + bが高いため 'black' と判定")
            return "black", avg_lab
        if b - a < thresholds["ba_black"]:
            print("    ルール6: b値がa値を下回っているため 'black' と判定")
            return "black", avg_lab
        print("    ルール : blackじゃないため 'green' と判定")
        return "green", avg_lab
        # if b > thresholds["b_black2"]:
        #     print("    ルール9: b値が高いため 'black' と判定")
        #     return "black", avg_lab
        # if b - a > thresholds["ba_green"]:
        #     print("    ルール6: b値がa値を上回っているため 'green' と判定")
        #     return "green", avg_lab
        # if b < thresholds["b_black"]:
        #     print("    ルール8: 緑っぽいけど 'black' と判定")
        #     return "black", avg_lab

        # print("    ルール2: a値が低いため 'green' と判定")
        # return "green", avg_lab
    elif a > thresholds["a_red"]:  # 赤側の閾値
        # ルール3: b値で赤とオレンジを区別
        # b > 128 が黄側, b < 128 が青側
        if a - b > thresholds["a_b_red"]:
            print("    ルール : a-bが大きいため 'red' と判定")
            return "red", avg_lab
        else:
            print("    ルール : a-bが小さいため 'orange'と判定")
            return "orange", avg_lab
        # if b > thresholds["b_orange"]:  # 黄色みが強ければオレンジ
        #     if b < thresholds["b_red"] and a > thresholds["a_orange"]:
        #         print("    ルール7: オレンジっぽいけど 'red' と判定")
        #         return "red", avg_lab
        #     print("    ルール3: a値が高く、b値も高いため 'orange' と判定")
        #     return "orange", avg_lab
        # else:  # それ以外は赤
        #     print(
        #         "    ルール4: a値が高く、b値がそれほど高くないため 'red' と判定"
        #     )
        #     return "red", avg_lab

    print("    ルール5: どのLABのルールにも一致しなかったため 'black' と判定")
    return "black", avg_lab


def preprocess_img(img):
    gray_region = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # --- ステップ1: カードマスクの作成 (変更なし) ---
    _, card_mask = cv2.threshold(gray_region, 160, 255, cv2.THRESH_BINARY)
    kernel_mask = np.ones((5, 5), np.uint8)
    card_mask = cv2.dilate(card_mask, kernel_mask, iterations=3)
    # cv2.imwrite(f"debug_card_mask_{player_name}.jpg", card_mask)

    # --- ステップ2: Cannyエッジ検出による前処理 ---
    # メディアンフィルタで元画像のノイズを軽く除去
    denoised_gray = cv2.medianBlur(gray_region, 3)
    thresholded = cv2.adaptiveThreshold(
        denoised_gray,
        255,
        cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
        cv2.THRESH_BINARY_INV,
        21,
        7,
    )
    preprocessed = cv2.bitwise_and(thresholded, thresholded, mask=card_mask)
    kernel_open = np.ones((3, 3), np.uint8)
    preprocessed = cv2.morphologyEx(preprocessed, cv2.MORPH_OPEN, kernel_open)
    return preprocessed


def filter_size(contours, scale, img):
    res = []
    for i, cnt in enumerate(contours):
        x, y, w, h = cv2.boundingRect(cnt)
        # サイズフィルタを適用
        # if y < 400 and w * h > 1500 * scale * scale:
        #     print(f"{w}x{h} at ({x}, {y})")
        if (
            40 * scale < h < 95 * scale
            and 25 * scale < w < 60 * scale
            and 0.25 < w / h < 0.9
            and 1500 * scale * scale < w * h < 4000 * scale * scale
        ):
            pad = 10
            cropped_img = img[
                max(0, y - pad) : min(y + h + pad, img.shape[0]),
                max(0, x - pad) : min(x + w + pad, img.shape[1]),
            ]
            no_padding_img = img[
                max(0, y) : min(y + h, img.shape[0]),
                max(0, x) : min(x + w, img.shape[1]),
            ]
            res.append(
                {
                    "img": cropped_img,
                    "no_pad": no_padding_img,
                    "pos": (x, y),
                    "size": (w, h),
                }
            )
    return res


def filter_thickness(
    candidates,
    scale,
):
    res = []
    for candidate in candidates:
        no_padding_img = candidate["no_pad"]
        cropped_img = candidate["img"]

        text_mask = get_not_white_mask(no_padding_img)

        # マスクを使って元のカラー画像から文字部分のみを抽出
        masked_char_image = cv2.bitwise_and(
            no_padding_img, no_padding_img, mask=text_mask
        )
        cropped_bin = cv2.cvtColor(masked_char_image, cv2.COLOR_BGR2GRAY)
        cropped_dist = cv2.distanceTransform(cropped_bin, cv2.DIST_L2, 3)
        _, max_val, _, _ = cv2.minMaxLoc(cropped_dist)

        timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S_%f")
        if IS_DEBUG:
            debug_dir = "debug_chars"
            if not os.path.exists(debug_dir):
                os.makedirs(debug_dir)
            debug_dir = "debug_chars"
            debug_filename = os.path.join(
                debug_dir, f"dist_char_{timestamp}.png"
            )

            # 画像を保存
            cv2.imwrite(debug_filename, cropped_dist)

        print(
            f"    候補 at ({candidate['pos']}) - 厚みスコア: {max_val:.2f}    {timestamp}"
        )
        if max_val > 12.0 * scale and max_val < 100000:
            # print("    -> スートと判断し除外")
            continue
        # else:
        # print("    -> ランク候補として採用")

        if cropped_img.size > 0:
            candidate["thickness"] = max_val
            res.append(candidate)
    return res


def filter_suit(candidates, suit_templates, threshold):
    filtered = []
    for candidate in candidates:
        is_suit = False

        candidate_gray = cv2.cvtColor(candidate["img"], cv2.COLOR_BGR2GRAY)

        for _, template in suit_templates.items():
            resized_template = cv2.resize(
                template, (candidate["size"][0], candidate["size"][1])
            )
            res = cv2.matchTemplate(
                candidate_gray, resized_template, cv2.TM_CCOEFF_NORMED
            )
            _, max_val, _, _ = cv2.minMaxLoc(res)

            if max_val > threshold:
                is_suit = True
                break
        if not is_suit:
            filtered.append(candidate)
    return filtered


def filter_vertically(candidates, scale):
    res = []
    for candidate in candidates:
        x, y = candidate["pos"]
        is_eliminated = False
        for _candidate in candidates:
            _x, _y = _candidate["pos"]

            if (
                (
                    x - _x < 35 * scale
                    and x - _x > -35 * scale
                    and y - _y > 40 * scale
                )
                or (
                    x - _x < 80 * scale
                    and x - _x > -80 * scale
                    and y - _y > 90 * scale
                )
                or (
                    x - _x < 300 * scale
                    and x - _x > -300 * scale
                    and y - _y > 170 * scale
                )
            ):
                is_eliminated = True
        if not is_eliminated:
            res.append(candidate)
    return res


def filter_uniform(candidates, threshold=20.0):
    res = []
    for candidate in candidates:
        text_mask = get_not_white_mask(candidate["no_pad"])
        if cv2.countNonZero(text_mask) > 20:
            lab_patch = cv2.cvtColor(candidate["no_pad"], cv2.COLOR_BGR2LAB)
            _, std_dev = cv2.meanStdDev(lab_patch, mask=text_mask)

            color_variance = math.sqrt(std_dev[1][0] ** 2 + std_dev[2][0] ** 2)

            # デバッグ用に標準偏差を出力
            print(
                f"    候補 at ({candidate['pos']}) - 色のばらつき: {color_variance:.2f}"
            )

            # 標準偏差が閾値より小さければ、色が均一であると判断
            if color_variance < threshold:
                res.append(candidate)
            else:
                print("    -> 絵柄と判断し、除外")
    return res


def find_rank_candidates(region_image, suit_templates, player_name, scale=1):
    print(scale)
    if region_image is None or region_image.size == 0:
        return []

    preprocessed = preprocess_img(region_image)
    if IS_DEBUG:
        cv2.imwrite(f"debug_ocr_preprocess_{player_name}.jpg", preprocessed)

    contours, _ = cv2.findContours(
        preprocessed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
    )

    SUIT_FILTER_THRESHOLD = 0.6

    candidates_size = filter_size(contours, scale, region_image)
    candidates_thickness = filter_thickness(candidates_size, scale)
    candidates_suit = filter_suit(
        candidates_thickness, suit_templates, SUIT_FILTER_THRESHOLD
    )
    candidates_vertically = filter_vertically(candidates_suit, scale)
    candidates_uniform = filter_uniform(candidates_vertically)

    save_img_with_rect(
        f"debug_ocr_thickness_{player_name}.jpg",
        region_image,
        candidates_thickness,
    )
    save_img_with_rect(
        f"debug_ocr_candidates_{player_name}.jpg",
        region_image,
        candidates_size,
    )
    save_img_with_rect(
        f"debug_filter_process_{player_name}.jpg",
        region_image,
        candidates_uniform,
    )

    print(
        f"フィルタリング過程を debug_filter_process_{player_name}.jpg に保存しました。"
    )
    print(len(candidates_uniform))
    return candidates_uniform


# ★★★ RGBで色を分析するヘルパー関数 ★★★
def get_avg_rgb_from_patch(patch):
    """画像パッチから文字部分の平均色(RGB)を計算する"""
    patch_gray = cv2.cvtColor(patch, cv2.COLOR_BGR2GRAY)
    _, text_mask = cv2.threshold(patch_gray, 180, 230, cv2.THRESH_BINARY_INV)
    if cv2.countNonZero(text_mask) == 0:
        return None
    # OpenCVの平均色はBGR順なので、RGB順に並べ替えて返す
    avg_bgr = cv2.mean(patch, mask=text_mask)[:3]
    return (avg_bgr[2], avg_bgr[1], avg_bgr[0])  # (R, G, B)


# ★★★ 最終改善版:Cannyエッジと輪郭階層を利用したカード認識関数 ★★★
def recognize_cards(region_image, suit_templates, player_name, trocr_pipeline):
    rank_candidates = find_rank_candidates(
        region_image, suit_templates, player_name
    )
    debug_region = region_image.copy()

    VALID_RANKS = [
        "A",
        "K",
        "Q",
        "J",
        "10",
        "9",
        "8",
        "7",
        "6",
        "5",
        "4",
        "3",
        "2",
    ]

    recognized_ranks = []
    if rank_candidates and trocr_pipeline:
        # 重複候補をマージする
        # ...(今回は省略。まずは検出できるかが重要)...

        candidate_pil_images = [
            Image.fromarray(cv2.cvtColor(c["img"], cv2.COLOR_BGR2RGB))
            for c in rank_candidates
        ]
        ocr_results = trocr_pipeline(candidate_pil_images)
        print([result[0]["generated_text"] for result in ocr_results])
        # ocr_results = trocr_pipeline(candidate_pil_images, generate_kwargs=generate_kwargs_sampling)

        for i, result in enumerate(ocr_results):
            text = result[0]["generated_text"].upper().strip()
            # TrOCRが誤認識しやすい文字を補正
            if text == "1O":
                text = "T"
            if text == "0" or text == "O":
                text = "T"

            if text in VALID_RANKS:
                candidate = rank_candidates[i]

                # --- 診断ログを出力 ---
                print(f"--- 診断中: ランク '{text}' at {candidate['pos']} ---")
                color_name = get_suit_from_image_rules(candidate["img"])
                print(f" -> 色判定結果: {color_name}")

                if color_name in SUITS_BY_COLOR:
                    suit = SUITS_BY_COLOR[color_name]
                    card_name = f"{suit}{text}"
                    is_duplicate = any(
                        math.sqrt(
                            (fc["pos"][0] - candidate["pos"][0]) ** 2
                            + (fc["pos"][1] - candidate["pos"][1]) ** 2
                        )
                        < 20
                        for fc in recognized_ranks
                    )
                    if not is_duplicate:
                        recognized_ranks.append(
                            {"name": card_name, "pos": candidate["pos"]}
                        )

    if IS_DEBUG:
        for card in recognized_ranks:
            cv2.putText(
                debug_region,
                card["name"],
                (card["pos"][0], card["pos"][1] - 10),
                cv2.FONT_HERSHEY_SIMPLEX,
                1.0,
                (255, 255, 0),
                2,
                cv2.LINE_AA,
            )
        cv2.imwrite(f"debug_detection_{player_name}.jpg", debug_region)
        print(
            f"{player_name} の検出結果を debug_detection_{player_name}.jpg に保存しました。"
        )
    return [c["name"] for c in recognized_ranks]


def main(image_path):
    suit_templates = load_suit_templates(SUIT_TEMPLATE_PATH)
    if not suit_templates:
        print(
            "エラー: templates/suits フォルダにスートのテンプレート画像が見つかりません。"
        )
        return

    image = cv2.imread(image_path)
    if image is None:
        return

    box = find_center_box(image)
    if box is None:
        return
    bx, by, bw, bh = box

    h, w, _ = image.shape
    margin = 200
    player_regions = {
        "north": image[0:by, :],
        "south": image[by + bh : h, :],
        "west": image[by - margin : by + bh + margin, 0:bx],
        "east": image[by - margin : by + bh + margin, bx + bw : w],
    }

    for player, region in player_regions.items():
        if region is None or region.size == 0:
            continue
        if player == "north":
            player_regions[player] = cv2.rotate(region, cv2.ROTATE_180)
        elif player == "east":
            player_regions[player] = cv2.rotate(
                region, cv2.ROTATE_90_CLOCKWISE
            )
        elif player == "west":
            player_regions[player] = cv2.rotate(
                region, cv2.ROTATE_90_COUNTERCLOCKWISE
            )

    all_hands = {}
    for player, region in player_regions.items():
        cards = recognize_cards(region, suit_templates, player)
        all_hands[player] = arrange_hand(cards)

    print("\n--- 最終識別結果 (ルールベース色判定) ---")
    for player, hand in all_hands.items():
        print(f"{player.capitalize()}: {', '.join(hand)}")
    print("---------------------------------------")


if __name__ == "__main__":
    IMAGE_FILE_PATH = "PXL_20250611_101254508.jpg"
    # if trocr_pipeline:
    #     main(IMAGE_FILE_PATH)
    # else:
    #     print("TrOCRパイプラインが初期化されていないため、処理を中止します。")
    # else:
    #     print("TrOCRパイプラインが初期化されていないため、処理を中止します。")