# Deploy skaldsong (https://gitea.phasefinal.com/vh/skaldsong) to a
# Docker host following the PFI /opt/docker/ convention (ana-docker by
# default, but the playbook works against any host with Docker in place).
#
# Registry-pull pattern, NOT build-on-host: vh/skaldsong's CI builds and
# pushes `gitea.phasefinal.com/vh/skaldsong:<sha>` (+ :latest) to the
# gitea registry; this playbook just pulls + recreates the container.
# Differs from althing-chamber / asset-engine playbooks which build the
# image on-host from a git clone — those are local-build stacks.
#
# Idempotent: rerunning is safe. Creates-gates skip work that's already
# done; `docker compose up -d` is itself idempotent (no restart unless
# compose content, env, or image SHA changed).
#
# Usage:
#   # CI passes the triggering commit SHA via --var ref=${{ github.sha }}:
#   scripts/elway ana-docker --playbook playbooks/deploy-skaldsong.yaml \
#       --var ref=<sha>
#
#   # Manual runs can pass a branch or tag (will resolve to the registry
#   # tag of that name — CI publishes :main and :v0.1.0 tags alongside
#   # the SHA-pin):
#   scripts/elway ana-docker --playbook playbooks/deploy-skaldsong.yaml \
#       --var ref=main
#
# Prereqs on the target host:
#   - Docker + docker compose plugin
#   - Target user (lkraven) is in the `docker` group
#   - `docker login gitea.phasefinal.com` has been done at least once
#     (credentials persist in /home/lkraven/.docker/config.json). CI's
#     workflow runs its own `docker login` step from REGISTRY_USER /
#     REGISTRY_TOKEN secrets; manual runs assume the host's auth is
#     already established.

vars:
  registry_image: gitea.phasefinal.com/vh/skaldsong
  ref: latest
  compose_dir: /opt/docker/compose/skaldsong
  db_dir: /opt/docker/conf/skaldsong/db
  runs_dir: /opt/docker/conf/skaldsong/runs
  host_port: "8300"

steps:
  # ── compose + state dirs ────────────────────────────────────────────
  - name: Ensure compose dir exists
    shell: mkdir -p {{ compose_dir }}
    creates: "{{ compose_dir }}"

  - name: Ensure DB dir exists
    # Created as lkraven (uid 1000 on these hosts), matching the
    # container's app user — no chown dance needed.
    shell: mkdir -p {{ db_dir }}
    creates: "{{ db_dir }}"

  - name: Ensure runs dir exists
    # Separate from db_dir so runs/ can later move to a bigger volume
    # without touching DB state.
    shell: mkdir -p {{ runs_dir }}
    creates: "{{ runs_dir }}"

  # ── deploy compose files ────────────────────────────────────────────
  - name: Upload compose.yaml
    upload:
      src: stacks/skaldsong/compose.yaml
      dest: "{{ compose_dir }}/compose.yaml"
      mode: "0644"

  - name: Seed .env from template (only if absent)
    upload:
      src: stacks/skaldsong/.env.example
      dest: "{{ compose_dir }}/.env"
      mode: "0644"
    when: "[ ! -f {{ compose_dir }}/.env ]"

  # ── pin SKALDSONG_IMAGE to the requested ref ────────────────────────
  - name: Set SKALDSONG_IMAGE pin to {{ ref }}
    # Idempotent: strip any existing SKALDSONG_IMAGE= line and append
    # the new one. CI passes --var ref=<sha>; manual runs pass branch /
    # tag names (CI publishes those as registry tags too).
    shell: |
      sed -i '/^SKALDSONG_IMAGE=/d' {{ compose_dir }}/.env
      echo "SKALDSONG_IMAGE={{ registry_image }}:{{ ref }}" >> {{ compose_dir }}/.env

  # ── pull + bring up ─────────────────────────────────────────────────
  - name: Pull image {{ registry_image }}:{{ ref }}
    # Explicit pull step (rather than relying on compose up's pull) so
    # any auth failure surfaces here, not buried in compose output.
    shell: docker pull {{ registry_image }}:{{ ref }}

  - name: docker compose up -d --force-recreate
    # --force-recreate picks up env changes even if the image SHA is
    # identical to what's running (rare but happens on env-only deploys).
    # --pull never: we just pulled explicitly above, so no need for
    # compose to re-check.
    shell: cd {{ compose_dir }} && docker compose up -d --force-recreate --pull never

  - name: Wait for skaldsong /health to respond
    # Container's healthcheck is internal; this host-side poll confirms
    # the published port is reachable + the FastAPI app finished booting.
    # Generous retry budget — first-deploy bootstrapping (DB migrations,
    # SPA asset indexing) can take 30s+.
    shell: |
      for i in $(seq 1 30); do
        curl -sf -o /dev/null http://localhost:{{ host_port }}/health && exit 0
        sleep 2
      done
      exit 1
    changed_when: "false"

verify:
  - name: skaldsong /health returns 200
    shell: curl -sf -o /dev/null http://localhost:{{ host_port }}/health
    changed_when: "false"

  - name: skaldsong container running
    # Grep for '^Up' (not 'healthy') — verify runs immediately after the
    # deploy step finishes, which is well before the container's
    # start_period (30s) elapses. The 'healthy' state from docker ps is
    # a delayed echo of the same /health probe verify 1/2 already
    # confirms, so racing the start_period here is bogus. Keep this
    # check to catch the "container exited entirely" case.
    shell: docker ps --filter name=^/skaldsong$ --format '{{.Status}}' | grep -q '^Up'
    changed_when: "false"
