#!/usr/bin/env python3
"""Tests for bitrise_deploy.py config/token/branch resolution."""

from __future__ import annotations

import json
import os
import sys
import tempfile
import unittest
from pathlib import Path
from unittest import mock

sys.path.insert(0, str(Path(__file__).resolve().parent))

import bitrise_deploy as bd  # noqa: E402


class _Env:
    def __init__(self, mapping):
        self._mapping = mapping
        self._prev = {}

    def __enter__(self):
        for k, v in self._mapping.items():
            self._prev[k] = os.environ.get(k)
            if v is None:
                os.environ.pop(k, None)
            else:
                os.environ[k] = v
        return self

    def __exit__(self, *_exc):
        for k, prev in self._prev.items():
            if prev is None:
                os.environ.pop(k, None)
            else:
                os.environ[k] = prev


class LoadConfigTests(unittest.TestCase):
    def test_loads_valid_config(self):
        with tempfile.TemporaryDirectory() as tmp:
            creds = Path(tmp) / "creds"
            creds.mkdir()
            (creds / "bitrise.json").write_text(json.dumps(
                {"enabled": True, "app_slug": "abc123", "workflow": "android-deploy"}))
            with _Env({"GITHUB_WORKSPACE": tmp}):
                cfg = bd.load_config()
            self.assertEqual(cfg["app_slug"], "abc123")

    def test_missing_file_exits(self):
        with tempfile.TemporaryDirectory() as tmp, _Env({"GITHUB_WORKSPACE": tmp}):
            with self.assertRaises(SystemExit):
                bd.load_config()

    def test_missing_app_slug_exits(self):
        with tempfile.TemporaryDirectory() as tmp:
            creds = Path(tmp) / "creds"
            creds.mkdir()
            (creds / "bitrise.json").write_text(json.dumps({"enabled": True}))
            with _Env({"GITHUB_WORKSPACE": tmp}):
                with self.assertRaises(SystemExit):
                    bd.load_config()


class ResolveTokenTests(unittest.TestCase):
    def test_env_token_wins(self):
        with _Env({"BITRISE_API_TOKEN": "env-tok"}):
            self.assertEqual(bd.resolve_token({"api_token": "cfg-tok"}), "env-tok")

    def test_falls_back_to_config_token(self):
        with _Env({"BITRISE_API_TOKEN": None}):
            self.assertEqual(bd.resolve_token({"api_token": "cfg-tok"}), "cfg-tok")

    def test_no_token_exits(self):
        with _Env({"BITRISE_API_TOKEN": None}):
            with self.assertRaises(SystemExit):
                bd.resolve_token({})


class ResolveBranchTests(unittest.TestCase):
    def test_config_branch_wins(self):
        with _Env({"GITHUB_REF": "refs/heads/feature"}):
            self.assertEqual(bd.resolve_branch({"branch": "release"}), "release")

    def test_derives_from_github_ref(self):
        with _Env({"GITHUB_REF": "refs/heads/main"}):
            self.assertEqual(bd.resolve_branch({}), "main")

    def test_defaults_to_main(self):
        with _Env({"GITHUB_REF": None}):
            self.assertEqual(bd.resolve_branch({}), "main")


class TriggerTests(unittest.TestCase):
    def test_trigger_posts_expected_body(self):
        captured = {}

        def fake_api(method, path, token, body=None):
            captured["method"] = method
            captured["path"] = path
            captured["body"] = body
            return {"status": "ok", "build_slug": "bs1", "build_number": 7}

        with _Env({"GITHUB_SHA": "deadbeef"}), \
                mock.patch.object(bd, "api", side_effect=fake_api):
            slug, num, wf = bd.trigger(
                {"app_slug": "abc", "workflow": "android-deploy"}, "tok", "main")
        self.assertEqual((slug, num, wf), ("bs1", 7, "android-deploy"))
        self.assertEqual(captured["method"], "POST")
        self.assertEqual(captured["path"], "/apps/abc/builds")
        self.assertEqual(captured["body"]["build_params"]["branch"], "main")
        self.assertEqual(captured["body"]["build_params"]["workflow_id"], "android-deploy")
        self.assertEqual(captured["body"]["build_params"]["commit_hash"], "deadbeef")

    def test_trigger_rejects_non_ok(self):
        with mock.patch.object(bd, "api", return_value={"status": "error", "message": "bad"}):
            with self.assertRaises(SystemExit):
                bd.trigger({"app_slug": "abc"}, "tok", "main")


if __name__ == "__main__":
    unittest.main()
