#!/usr/bin/env python3
"""Tests for Gitea API module."""

import json
import os
import sys
import unittest
from unittest.mock import patch, MagicMock
from http.server import HTTPServer, BaseHTTPRequestHandler
import threading

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from tools.gitea_api import GiteaClient, GiteaAPIError


class TestGiteaClientInit(unittest.TestCase):
    """Test client initialization."""

    def test_init_with_explicit_params(self):
        c = GiteaClient(base_url="http://localhost:3000", token="test123")
        self.assertEqual(c.base_url, "http://localhost:3000")
        self.assertEqual(c.token, "test123")

    def test_init_strips_trailing_slash(self):
        c = GiteaClient(base_url="http://localhost:3000/", token="test")
        self.assertEqual(c.base_url, "http://localhost:3000")

    def test_init_no_token_raises(self):
        with patch.dict(os.environ, {}, clear=True):
            os.environ.pop("GITEA_TOKEN", None)
            with self.assertRaises(ValueError):
                GiteaClient(token="")

    @patch.dict(os.environ, {"GITEA_TOKEN": "envtoken123", "GITEA_URL": "http://env:3000"})
    def test_init_from_env(self):
        c = GiteaClient()
        self.assertEqual(c.token, "envtoken123")
        self.assertEqual(c.base_url, "http://env:3000")

    def test_headers(self):
        c = GiteaClient(base_url="http://test", token="tok123")
        h = c._headers()
        self.assertEqual(h["Authorization"], "token tok123")
        self.assertEqual(h["Content-Type"], "application/json")


class TestGiteaAPIError(unittest.TestCase):
    """Test error class."""

    def test_error_message(self):
        e = GiteaAPIError(401, "Unauthorized", "http://test/api")
        self.assertEqual(e.status_code, 401)
        self.assertIn("401", str(e))
        self.assertIn("Unauthorized", str(e))

    def test_error_no_url(self):
        e = GiteaAPIError(500, "Server Error")
        self.assertEqual(e.url, "")


class MockGiteaHandler(BaseHTTPRequestHandler):
    """Mock Gitea API server for integration tests."""

    def do_GET(self):
        if self.path == "/api/v1/user":
            self._json_response(200, {"login": "ezra", "id": 19})
        elif self.path.startswith("/api/v1/repos/ezra/test/issues"):
            self._json_response(200, [
                {"number": 1, "title": "Test issue", "state": "open", "labels": []},
            ])
        elif self.path.startswith("/api/v1/repos/ezra/test/labels"):
            self._json_response(200, [
                {"id": 1, "name": "bug", "color": "#e11d48"},
            ])
        elif self.path.startswith("/api/v1/repos/ezra/test/milestones"):
            self._json_response(200, [])
        elif self.path == "/api/v1/user/repos?limit=50":
            self._json_response(200, [{"full_name": "ezra/test", "description": "test repo"}])
        elif self.path == "/api/v1/repos/ezra/test":
            self._json_response(200, {"full_name": "ezra/test"})
        elif self.path == "/api/v1/repos/ezra/notfound":
            self._json_response(404, {"message": "not found"})
        else:
            self._json_response(404, {"message": "not found"})

    def do_POST(self):
        content_len = int(self.headers.get("Content-Length", 0))
        body = json.loads(self.rfile.read(content_len)) if content_len else {}

        if self.path == "/api/v1/repos/ezra/test/issues":
            self._json_response(201, {
                "number": 42, "title": body.get("title", ""), "state": "open",
            })
        elif self.path.startswith("/api/v1/repos/ezra/test/issues/") and "/comments" in self.path:
            self._json_response(201, {"id": 1, "body": body.get("body", "")})
        elif self.path == "/api/v1/repos/ezra/test/labels":
            self._json_response(201, {"id": 2, "name": body.get("name", ""), "color": body.get("color", "")})
        elif self.path == "/api/v1/repos/ezra/test/milestones":
            self._json_response(201, {"id": 1, "title": body.get("title", "")})
        else:
            self._json_response(404, {"message": "not found"})

    def do_PATCH(self):
        content_len = int(self.headers.get("Content-Length", 0))
        body = json.loads(self.rfile.read(content_len)) if content_len else {}

        if "/issues/" in self.path:
            self._json_response(200, {"number": 1, "state": body.get("state", "open")})
        else:
            self._json_response(404, {"message": "not found"})

    def _json_response(self, code, data):
        self.send_response(code)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        self.wfile.write(json.dumps(data).encode())

    def log_message(self, *args):
        pass  # Silence request logging


class TestGiteaClientIntegration(unittest.TestCase):
    """Integration tests with mock HTTP server."""

    @classmethod
    def setUpClass(cls):
        cls.server = HTTPServer(("127.0.0.1", 0), MockGiteaHandler)
        cls.port = cls.server.server_address[1]
        cls.thread = threading.Thread(target=cls.server.serve_forever)
        cls.thread.daemon = True
        cls.thread.start()
        cls.client = GiteaClient(
            base_url=f"http://127.0.0.1:{cls.port}",
            token="testtoken",
            max_retries=1,
        )

    @classmethod
    def tearDownClass(cls):
        cls.server.shutdown()

    def test_whoami(self):
        user = self.client.whoami()
        self.assertEqual(user["login"], "ezra")

    def test_validate_token(self):
        ok, name = self.client.validate_token()
        self.assertTrue(ok)
        self.assertEqual(name, "ezra")

    def test_list_issues(self):
        issues = self.client.list_issues("ezra", "test")
        self.assertEqual(len(issues), 1)
        self.assertEqual(issues[0]["title"], "Test issue")

    def test_create_issue(self):
        issue = self.client.create_issue("ezra", "test", "New issue", "Body text")
        self.assertEqual(issue["number"], 42)

    def test_close_issue(self):
        result = self.client.close_issue("ezra", "test", 1)
        self.assertEqual(result["state"], "closed")

    def test_add_comment(self):
        result = self.client.add_comment("ezra", "test", 1, "test comment")
        self.assertEqual(result["body"], "test comment")

    def test_list_labels(self):
        labels = self.client.list_labels("ezra", "test")
        self.assertEqual(len(labels), 1)
        self.assertEqual(labels[0]["name"], "bug")

    def test_create_label(self):
        label = self.client.create_label("ezra", "test", "feature", "0ea5e9")
        self.assertEqual(label["name"], "feature")

    def test_ensure_label_existing(self):
        label = self.client.ensure_label("ezra", "test", "bug", "e11d48")
        self.assertEqual(label["name"], "bug")

    def test_ensure_label_new(self):
        label = self.client.ensure_label("ezra", "test", "newlabel", "00ff00")
        self.assertEqual(label["name"], "newlabel")

    def test_list_repos(self):
        repos = self.client.list_repos()
        self.assertEqual(len(repos), 1)

    def test_get_repo(self):
        repo = self.client.get_repo("ezra", "test")
        self.assertEqual(repo["full_name"], "ezra/test")

    def test_404_raises(self):
        with self.assertRaises(GiteaAPIError) as ctx:
            self.client.get_repo("ezra", "notfound")
        self.assertEqual(ctx.exception.status_code, 404)

    def test_create_milestone(self):
        ms = self.client.create_milestone("ezra", "test", "v1.0")
        self.assertEqual(ms["title"], "v1.0")

    def test_ensure_milestone_new(self):
        ms = self.client.ensure_milestone("ezra", "test", "v2.0")
        self.assertEqual(ms["title"], "v2.0")


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