from pathlib import Path

import cv2
import numpy as np


ROOT = Path("/home/vela/workspace/domain/tn-silver/web/tn-site")
SOURCE_IMAGE = Path("/home/vela/Music/temp6.png")
RUNTIME = ROOT / ".runtime/image-edit-temp6"
OUTPUT = ROOT / "output/imagegen/temp6-ring"
MUSIC = Path("/home/vela/Music")

CROP_X = 900
CROP_Y = 300
CROP_W = 420
CROP_H = 400

# API-output coordinates for the camera-facing metal surface of each new ring.
# Each tuple is: center x/y, long edge, short edge, clockwise angle in degrees.
SOURCE_BANDS = {
    "01": ((182.0, 179.0), (17.0, 5.0), 45.0),
    "02": ((217.0, 198.0), (25.0, 8.0), 46.0),
    "03": ((204.0, 200.0), (24.0, 9.0), 46.0),
    "04": ((195.0, 192.0), (21.0, 7.0), 46.0),
    "05": ((199.0, 200.0), (20.0, 7.0), 46.0),
    "06": ((198.0, 185.0), (24.0, 9.0), 45.0),
    "07": ((176.5, 201.0), (17.0, 6.0), 45.0),
    "08": ((189.0, 166.0), (22.0, 8.0), 45.0),
}

# Destination dimensions retain the intended size differences while staying
# within the visible width of the original index finger.
DESTINATION_BANDS = {
    "01": ((195.0, 204.0), (16.0, 5.0), 45.0),
    "02": ((195.0, 204.0), (17.0, 7.0), 45.0),
    "03": ((195.0, 204.0), (17.0, 8.0), 45.0),
    "04": ((195.0, 204.0), (16.0, 6.0), 45.0),
    "05": ((195.0, 204.0), (16.0, 6.0), 45.0),
    "06": ((195.0, 204.0), (17.0, 8.0), 45.0),
    "07": ((195.0, 204.0), (16.0, 6.0), 45.0),
    "08": ((195.0, 204.0), (17.0, 7.0), 45.0),
}


def load_image(path: Path) -> np.ndarray:
    image = cv2.imread(str(path), cv2.IMREAD_COLOR)
    if image is None:
        raise RuntimeError(f"Could not read image: {path}")
    return image


def ordered_box(center, size, angle) -> np.ndarray:
    points = cv2.boxPoints((center, size, angle)).astype(np.float32)
    centroid = points.mean(axis=0)
    angles = np.arctan2(points[:, 1] - centroid[1], points[:, 0] - centroid[0])
    return points[np.argsort(angles)]


def main() -> None:
    full = load_image(SOURCE_IMAGE)
    if full.shape[:2] != (992, 1586):
        raise RuntimeError(f"Unexpected source dimensions: {full.shape[1]}x{full.shape[0]}")

    original_crop = full[CROP_Y : CROP_Y + CROP_H, CROP_X : CROP_X + CROP_W].copy()
    composite_dir = RUNTIME / "band-composites"
    mask_dir = RUNTIME / "band-masks"
    composite_dir.mkdir(parents=True, exist_ok=True)
    mask_dir.mkdir(parents=True, exist_ok=True)
    OUTPUT.mkdir(parents=True, exist_ok=True)

    for variant, source_spec in SOURCE_BANDS.items():
        source = load_image(RUNTIME / f"api-edits/variant-{variant}-420.png")
        source_quad = ordered_box(*source_spec)
        destination_quad = ordered_box(*DESTINATION_BANDS[variant])
        transform = cv2.getPerspectiveTransform(source_quad, destination_quad)

        warped = cv2.warpPerspective(
            source,
            transform,
            (CROP_W, CROP_H),
            flags=cv2.INTER_CUBIC,
            borderMode=cv2.BORDER_CONSTANT,
            borderValue=(0, 0, 0),
        )

        source_mask = np.zeros((CROP_H, CROP_W), dtype=np.uint8)
        cv2.fillConvexPoly(source_mask, np.round(source_quad).astype(np.int32), 255, cv2.LINE_AA)
        warped_mask = cv2.warpPerspective(
            source_mask,
            transform,
            (CROP_W, CROP_H),
            flags=cv2.INTER_CUBIC,
            borderMode=cv2.BORDER_CONSTANT,
            borderValue=0,
        )
        warped_mask = cv2.GaussianBlur(warped_mask, (0, 0), 0.55)
        alpha = warped_mask.astype(np.float32)[..., None] / 255.0
        composited_crop = np.clip(
            warped.astype(np.float32) * alpha
            + original_crop.astype(np.float32) * (1.0 - alpha),
            0,
            255,
        ).astype(np.uint8)

        cv2.imwrite(str(mask_dir / f"mask-{variant}.png"), warped_mask)
        cv2.imwrite(str(composite_dir / f"hand-variant-{variant}.png"), composited_crop)

        composited_full = full.copy()
        composited_full[
            CROP_Y : CROP_Y + CROP_H,
            CROP_X : CROP_X + CROP_W,
        ] = composited_crop

        output_path = OUTPUT / f"temp6-index-ring-v{variant}.png"
        music_path = MUSIC / output_path.name
        cv2.imwrite(str(output_path), composited_full)
        cv2.imwrite(str(music_path), composited_full)


if __name__ == "__main__":
    main()
