from __future__ import annotations

import argparse
import json
import os
import sys
from pathlib import Path
from urllib import error, parse, request


def _required_env(name: str) -> str:
    value = os.getenv(name, '').strip()
    if not value:
        raise SystemExit(f'Missing required environment variable: {name}')
    return value


def _headers(token: str) -> dict[str, str]:
    return {
        'Authorization': f'token {token}',
        'Content-Type': 'application/json',
        'Accept': 'application/json',
    }


def _call_json(method: str, url: str, token: str, payload: dict) -> dict:
    data = json.dumps(payload, ensure_ascii=False).encode('utf-8')
    req = request.Request(url, data=data, headers=_headers(token), method=method)
    try:
        with request.urlopen(req, timeout=30) as resp:
            return json.loads(resp.read().decode('utf-8'))
    except error.HTTPError as exc:
        body = exc.read().decode('utf-8', errors='replace')
        raise SystemExit(f'Gitea API error {exc.code}: {body}') from exc
    except error.URLError as exc:
        raise SystemExit(f'Failed to reach Gitea API: {exc}') from exc


def _load_text(path: Path) -> str:
    return path.read_text(encoding='utf-8')


def _repo_api_base(base_url: str, repo: str) -> str:
    owner, name = repo.split('/', 1)
    return f"{base_url.rstrip('/')}/api/v1/repos/{parse.quote(owner)}/{parse.quote(name)}"


def create_issue(base_url: str, token: str, repo: str, title: str, body: str) -> dict:
    url = f'{_repo_api_base(base_url, repo)}/issues'
    return _call_json('POST', url, token, {'title': title, 'body': body})


def create_comment(base_url: str, token: str, repo: str, issue_number: int, body: str) -> dict:
    url = f'{_repo_api_base(base_url, repo)}/issues/{issue_number}/comments'
    return _call_json('POST', url, token, {'body': body})


def update_issue(base_url: str, token: str, repo: str, issue_number: int, title: str | None, body: str | None) -> dict:
    payload: dict[str, str] = {}
    if title is not None:
        payload['title'] = title
    if body is not None:
        payload['body'] = body
    url = f'{_repo_api_base(base_url, repo)}/issues/{issue_number}'
    return _call_json('PATCH', url, token, payload)


def main() -> None:
    parser = argparse.ArgumentParser(description='Create or update Gitea issues/comments.')
    sub = parser.add_subparsers(dest='command', required=True)

    common = argparse.ArgumentParser(add_help=False)
    common.add_argument('--repo', required=True, help='owner/repo')
    common.add_argument('--body-file', required=True, help='UTF-8 markdown file path')

    p_issue = sub.add_parser('create-issue', parents=[common])
    p_issue.add_argument('--title', required=True)

    p_comment = sub.add_parser('create-comment', parents=[common])
    p_comment.add_argument('--issue-number', type=int, required=True)

    p_update = sub.add_parser('update-issue', parents=[common])
    p_update.add_argument('--issue-number', type=int, required=True)
    p_update.add_argument('--title')

    args = parser.parse_args()

    base_url = _required_env('GITEA_URL')
    token = _required_env('GITEA_TOKEN')
    body = _load_text(Path(args.body_file))

    if args.command == 'create-issue':
        result = create_issue(base_url, token, args.repo, args.title, body)
    elif args.command == 'create-comment':
        result = create_comment(base_url, token, args.repo, args.issue_number, body)
    else:
        result = update_issue(base_url, token, args.repo, args.issue_number, args.title, body)

    json.dump(result, sys.stdout, ensure_ascii=False, indent=2)
    sys.stdout.write('\n')


if __name__ == '__main__':
    main()
