#!/usr/bin/env python3
"""Build all microservice images via docker buildx bake."""

import json
import os
import subprocess
import sys
from glob import glob
from pathlib import Path

ROOT_DIR = Path(__file__).resolve().parent.parent.parent
IMAGE_PREFIX = os.environ.get("IMAGE_PREFIX", "juwan")
IMAGE_TAG = sys.argv[1] if len(sys.argv) > 1 else "dev"
BAKE_BATCH_SIZE = int(os.environ.get("BAKE_BATCH_SIZE", "8"))

DOCKERFILE_TPL = """\
# syntax=docker/dockerfile:1.7
FROM golang:1.26-alpine AS builder
RUN apk add --no-cache tzdata
WORKDIR /build
ARG GOPROXY="https://goproxy.cn,direct"
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \\
    --mount=type=cache,target=/root/.cache/go-build \\
    go build -ldflags="-s -w" -o /app/main ./{service_dir}

FROM alpine:latest
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
ENV TZ=Asia/Shanghai
WORKDIR /app
COPY --from=builder /app/main /app/main
COPY {service_dir}/etc /app/etc
CMD ["./main", "-f", "etc/{config_name}"]
"""

DOCKERFILE_NO_CONFIG_TPL = """\
# syntax=docker/dockerfile:1.7
FROM golang:1.26-alpine AS builder
RUN apk add --no-cache tzdata
WORKDIR /build
ARG GOPROXY="https://goproxy.cn,direct"
COPY go.mod go.sum ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download
COPY . .
RUN --mount=type=cache,target=/go/pkg/mod \\
    --mount=type=cache,target=/root/.cache/go-build \\
    go build -ldflags="-s -w" -o /app/main ./{service_dir}

FROM alpine:latest
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai
ENV TZ=Asia/Shanghai
WORKDIR /app
COPY --from=builder /app/main /app/main
CMD ["./main"]
"""


def discover_targets():
    targets = {}
    for service_dir in sorted(glob(str(ROOT_DIR / "app" / "*" / "*"))):
        service_dir_rel = Path(service_dir).relative_to(ROOT_DIR).as_posix()
        service_type = Path(service_dir).name
        if service_type not in ("api", "rpc", "mq", "adapter"):
            continue
        go_files = glob(os.path.join(service_dir, "*.go"))
        if not go_files or not any(
            "package main" in Path(f).read_text(encoding="utf-8") for f in go_files
        ):
            continue

        yamls = glob(os.path.join(service_dir, "etc", "*.yaml"))
        service_name = Path(service_dir).parent.name
        target_name = f"{service_name}-{service_type}"

        if yamls:
            config_name = Path(yamls[0]).name
            dockerfile = DOCKERFILE_TPL.format(
                service_dir=service_dir_rel, config_name=config_name
            )
        else:
            dockerfile = DOCKERFILE_NO_CONFIG_TPL.format(service_dir=service_dir_rel)

        targets[target_name] = {
            "dockerfile-inline": dockerfile,
            "tags": [f"{IMAGE_PREFIX}/{target_name}:{IMAGE_TAG}"],
        }
    return targets


def frontend_target():
    frontend_root = ROOT_DIR / "frontend"
    if not (frontend_root / "Dockerfile").exists():
        return None
    return {
        "context": "frontend",
        "dockerfile": "Dockerfile",
        "tags": [f"{IMAGE_PREFIX}/frontend:{IMAGE_TAG}"],
    }


def main():
    os.chdir(ROOT_DIR)
    targets = discover_targets()
    ft = frontend_target()
    if ft is not None:
        targets["frontend"] = ft
    if not targets:
        print("No targets found")
        sys.exit(1)

    bake = {
        "group": {"default": {"targets": list(targets.keys())}},
        "target": targets,
    }

    bakefile = ROOT_DIR / ".bake.json"
    try:
        bakefile.write_text(json.dumps(bake, indent=2))
        print(f"Generated {len(targets)} targets -> {bakefile}")

        names = list(targets.keys())
        for i in range(0, len(names), BAKE_BATCH_SIZE):
            batch = names[i : i + BAKE_BATCH_SIZE]
            subprocess.run(
                ["docker", "buildx", "bake", "--load", "-f", str(bakefile), *batch],
                check=True,
            )
    finally:
        bakefile.unlink(missing_ok=True)


main()
