#!/usr/bin/env bash
set -euo pipefail

SERVER="git.nytegear.com"
OWNER="aargonian"
REMOTE="origin"
NAME=""
DESCRIPTION=""
PRIVATE=false
PROTOCOL="https"
BRANCH=""
INITIAL_COMMIT=false
EXISTING_OK=false
REPLACE_REMOTE=false
PUSH_TAGS=true
DRY_RUN=false

usage() {
    cat <<'EOF'
Usage:
  rpush [options]

Create a new Gitea repo for the current directory, add it as a Git remote,
and push the current branch.

Options:
  --name NAME             Remote repo name. Defaults to current directory name.
  --description TEXT      Repository description.
  --public                Create public repo. Default.
  --private               Create private repo. Default is public.
  --remote NAME           Git remote name. Default: origin.
  --branch NAME           Branch to push. Defaults to current branch.
  --https                 Use HTTPS remote URL. Default.
  --ssh                   Use SSH remote URL instead of SSH.
  --initial-commit        If the repo has no commits, git add -A and commit.
  --existing-ok           Continue if the Gitea repo already exists.
  --replace-remote        Replace local remote URL if the remote name exists.
  --no-tags               Do not push tags.
  --dry-run               Print what would happen, but do not create or push.
  -h, --help              Show this help.

Auth:
  Set GITEA_TOKEN before running, or the script will prompt for it.

Examples:
  rpush
  rpush --name my-custom-repo
  rpush --name my-custom-repo --public
  rpush --initial-commit
EOF
}

die() {
    printf 'rpush: error: %s\n' "$*" >&2
    exit 1
}

info() {
    printf 'rpush: %s\n' "$*" >&2
}

need() {
    command -v "$1" >/dev/null 2>&1 || die "missing required command: $1"
}

json_payload() {
    python3 - "$NAME" "$DESCRIPTION" "$PRIVATE" "$BRANCH" <<'PY'
import json
import sys

name, description, private, branch = sys.argv[1:]
payload = {
    "name": name,
    "description": description,
    "private": private.lower() == "true",
    "auto_init": False,
}
if branch:
    payload["default_branch"] = branch

print(json.dumps(payload, separators=(",", ":")))
PY
}

api_message() {
    local file="$1"

    python3 - "$file" <<'PY'
import json
import sys
from pathlib import Path

path = Path(sys.argv[1])
text = path.read_text(errors="replace").strip()

try:
    data = json.loads(text) if text else {}
except Exception:
    print(text[:4000] if text else "(empty response)")
    raise SystemExit

if isinstance(data, dict):
    print(data.get("message") or json.dumps(data, indent=2))
else:
    print(json.dumps(data, indent=2))
PY
}

parse_args() {
    while (($#)); do
        case "$1" in
            --name)
                [[ $# -ge 2 ]] || die "--name requires a value"
                NAME="$2"
                shift 2
                ;;
            --description)
                [[ $# -ge 2 ]] || die "--description requires a value"
                DESCRIPTION="$2"
                shift 2
                ;;
            --public)
                PRIVATE=false
                shift
                ;;
            --private)
                PRIVATE=true
                shift
                ;;
            --remote)
                [[ $# -ge 2 ]] || die "--remote requires a value"
                REMOTE="$2"
                shift 2
                ;;
            --branch)
                [[ $# -ge 2 ]] || die "--branch requires a value"
                BRANCH="$2"
                shift 2
                ;;
            --https)
                PROTOCOL="https"
                shift
                ;;
            --ssh)
                PROTOCOL="ssh"
                shift
                ;;
            --initial-commit)
                INITIAL_COMMIT=true
                shift
                ;;
            --existing-ok)
                EXISTING_OK=true
                shift
                ;;
            --replace-remote)
                REPLACE_REMOTE=true
                shift
                ;;
            --no-tags)
                PUSH_TAGS=false
                shift
                ;;
            --dry-run)
                DRY_RUN=true
                shift
                ;;
            -h|--help)
                usage
                exit 0
                ;;
            *)
                die "unknown argument: $1"
                ;;
        esac
    done
}

validate_repo_name() {
    [[ -n "$NAME" ]] || die "repo name is empty"
    [[ "$NAME" != "." && "$NAME" != ".." ]] || die "invalid repo name: $NAME"
    [[ "$NAME" != *"/"* ]] || die "repo name must not contain /"

    if [[ "$NAME" =~ [[:space:]] ]]; then
        die "repo name contains whitespace; pass a cleaner --name"
    fi

    if [[ ! "$NAME" =~ ^[A-Za-z0-9._-]+$ ]]; then
        die "repo name contains unsupported characters; use letters, numbers, dot, underscore, or dash"
    fi
}

ensure_git_repo_root() {
    local cwd root

    cwd="$(pwd -P)"

    if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
        root="$(git rev-parse --show-toplevel)"
        root="$(cd "$root" && pwd -P)"

        [[ "$cwd" == "$root" ]] || die "current directory is inside a Git repo but not at its root: $root"
    else
        info "initializing Git repository"
        if [[ -n "$BRANCH" ]]; then
            git init -b "$BRANCH"
        else
            git init
        fi
    fi
}

ensure_commit_exists() {
    if git rev-parse --verify HEAD >/dev/null 2>&1; then
        return
    fi

    if [[ "$INITIAL_COMMIT" == true ]]; then
        info "creating initial commit"
        git add -A
        git diff --cached --quiet && die "nothing staged; directory appears empty"
        git commit -m "Initial commit"
    else
        die "repository has no commits. Commit manually first, or rerun with --initial-commit"
    fi
}

detect_branch() {
    if [[ -z "$BRANCH" ]]; then
        BRANCH="$(git branch --show-current)"
    fi

    [[ -n "$BRANCH" ]] || die "could not determine branch; are you in detached HEAD?"
}

remote_url() {
    case "$PROTOCOL" in
        ssh)
            printf 'git@%s:%s/%s.git\n' "$SERVER" "$OWNER" "$NAME"
            ;;
        https)
            printf 'https://%s/%s/%s.git\n' "$SERVER" "$OWNER" "$NAME"
            ;;
        *)
            die "unsupported protocol: $PROTOCOL"
            ;;
    esac
}

create_gitea_repo() {
    local api_url payload tmp_body tmp_cfg status

    api_url="https://${SERVER}/api/v1/user/repos"
    payload="$(json_payload)"
    tmp_body="$(mktemp)"
    tmp_cfg="$(mktemp)"
    chmod 600 "$tmp_cfg"

    {
        printf 'header = "Accept: application/json"\n'
        printf 'header = "Content-Type: application/json"\n'
        printf 'header = "Authorization: token %s"\n' "$GITEA_TOKEN"
    } > "$tmp_cfg"

    if [[ "$DRY_RUN" == true ]]; then
        info "would POST $api_url"
        info "would create repo: $OWNER/$NAME private=$PRIVATE branch=$BRANCH"
        rm -f "$tmp_body" "$tmp_cfg"
        return
    fi

    if ! status="$(
        curl --silent --show-error \
            --output "$tmp_body" \
            --write-out '%{http_code}' \
            --request POST \
            --config "$tmp_cfg" \
            --data-binary "$payload" \
            "$api_url"
    )"; then
        rm -f "$tmp_body" "$tmp_cfg"
        die "curl failed while creating remote repo"
    fi

    case "$status" in
        201)
            info "created remote repo: $OWNER/$NAME"
            ;;
        409)
            if [[ "$EXISTING_OK" == true ]]; then
                info "remote repo already exists; continuing because --existing-ok was set"
            else
                rm -f "$tmp_body" "$tmp_cfg"
                die "remote repo already exists: $OWNER/$NAME. Use --existing-ok to push anyway"
            fi
            ;;
        *)
            printf 'rpush: Gitea API returned HTTP %s: ' "$status" >&2
            api_message "$tmp_body" >&2
            rm -f "$tmp_body" "$tmp_cfg"
            exit 1
            ;;
    esac

    rm -f "$tmp_body" "$tmp_cfg"
}

configure_remote() {
    local url existing_url

    url="$(remote_url)"

    if git remote get-url "$REMOTE" >/dev/null 2>&1; then
        existing_url="$(git remote get-url "$REMOTE")"

        if [[ "$existing_url" == "$url" ]]; then
            info "remote $REMOTE already points to $url"
            return
        fi

        if [[ "$REPLACE_REMOTE" == true ]]; then
            info "replacing remote $REMOTE URL"
            [[ "$DRY_RUN" == true ]] || git remote set-url "$REMOTE" "$url"
            return
        fi

        die "remote $REMOTE already exists with different URL: $existing_url. Use --replace-remote"
    fi

    info "adding remote $REMOTE -> $url"
    [[ "$DRY_RUN" == true ]] || git remote add "$REMOTE" "$url"
}

push_repo() {
    if [[ "$DRY_RUN" == true ]]; then
        info "would push branch $BRANCH to $REMOTE"
        [[ "$PUSH_TAGS" == true ]] && info "would push tags"
        return
    fi

    info "pushing branch $BRANCH to $REMOTE"
    git push -u "$REMOTE" "$BRANCH"

    if [[ "$PUSH_TAGS" == true ]]; then
        info "pushing tags to $REMOTE"
        git push "$REMOTE" --tags
    fi
}

main() {
    parse_args "$@"

    need git
    need curl
    need python3

    if [[ -z "$NAME" ]]; then
        NAME="$(basename "$(pwd -P)")"
    fi

    validate_repo_name
    ensure_git_repo_root
    ensure_commit_exists
    detect_branch

    if [[ -z "${GITEA_TOKEN:-}" && "$DRY_RUN" != true ]]; then
        read -r -s -p "Gitea access token: " GITEA_TOKEN
        printf '\n' >&2
    fi

    [[ "$DRY_RUN" == true || -n "${GITEA_TOKEN:-}" ]] || die "GITEA_TOKEN is empty"

    create_gitea_repo
    configure_remote
    push_repo

    info "done: $OWNER/$NAME"
}

main "$@"
