from ..decorators import handle_error as handle_error
from ..io.path import super_open as super_open
from ..print.message import info as info, progress as progress, warning as warning
from .cd_utils import clean_version as clean_version, format_changelog as format_changelog, version_to_float as version_to_float
from collections.abc import Callable as Callable

def run_git_command(args: list[str], cwd: str | None = None) -> str:
    """ Run a git command and return the output.

\tArgs:
\t\targs\t(list[str]):\tGit command arguments (without 'git' prefix)
\t\tcwd\t\t(str | None):\tWorking directory for the command
\tReturns:
\t\tstr:\tCommand output (stdout)
\tRaises:
\t\t:py:exc:`RuntimeError`: If the git command fails
\t"""
def get_local_tags(cwd: str | None = None) -> list[tuple[str, str]]:
    """ Get all tags from the local git repository, sorted by version.

\tArgs:
\t\tcwd\t(str | None):\tWorking directory for the git command
\tReturns:
\t\tlist[tuple[str, str]]:\tList of (tag_name, commit_sha) tuples, sorted by version (newest first)
\t"""
def get_latest_tag(cwd: str | None = None, exclude_version: str | None = None) -> tuple[str, str] | tuple[None, None]:
    """ Get the latest tag from the local git repository.

\tArgs:
\t\tcwd\t\t\t\t(str | None):\tWorking directory for the git command
\t\texclude_version\t(str | None):\tVersion to exclude from the search
\tReturns:
\t\ttuple[str, str] | tuple[None, None]:\t(tag_name, commit_sha) or (None, None) if no tags exist
\t"""
def get_commits_since_tag(tag: str, cwd: str | None = None) -> list[tuple[str, str]]:
    """ Get all commits since a specific tag.

\tArgs:
\t\ttag\t(str):\t\t\tTag name to start from (exclusive)
\t\tcwd\t(str | None):\tWorking directory for the git command
\tReturns:
\t\tlist[tuple[str, str]]:\tList of (sha, message) tuples
\t"""
def get_commits_since_date(date_str: str, cwd: str | None = None) -> list[tuple[str, str]]:
    """ Get all commits since a specific date.

\tArgs:
\t\tdate_str\t(str):\t\t\tDate string (supports multiple formats via dateutil)
\t\tcwd\t\t\t(str | None):\tWorking directory for the git command
\tReturns:
\t\tlist[tuple[str, str]]:\tList of (sha, message) tuples
\t"""
def get_commits_since_commit(commit_sha: str, cwd: str | None = None) -> list[tuple[str, str]]:
    """ Get all commits since a specific commit (exclusive).

\tArgs:
\t\tcommit_sha\t(str):\t\t\tCommit SHA to start from (this commit is excluded)
\t\tcwd\t\t\t(str | None):\tWorking directory for the git command
\tReturns:
\t\tlist[tuple[str, str]]:\tList of (sha, message) tuples
\t"""
def parse_date_fallback(date_str: str) -> str:
    ''' Parse a date string without dateutil, trying common formats.

\tArgs:
\t\tdate_str\t(str):\tDate string to parse
\tReturns:
\t\tstr:\tISO 8601 formatted date string
\tRaises:
\t\t:py:exc:`ValueError`:\tIf the date cannot be parsed

\t>>> parse_date_fallback("2026/01/15")
\t\'2026-01-15T00:00:00\'
\t>>> parse_date_fallback("2026-01-15")
\t\'2026-01-15T00:00:00\'
\t>>> parse_date_fallback("2026-01-15 14:30:00")
\t\'2026-01-15T14:30:00\'
\t>>> parse_date_fallback("2026-01-15T14:30:00")
\t\'2026-01-15T14:30:00\'
\t'''
def parse_commit_log(output: str) -> list[tuple[str, str]]:
    """ Parse git log output into a list of (sha, message) tuples.

\tArgs:
\t\toutput\t(str):\tOutput from git log command
\tReturns:
\t\tlist[tuple[str, str]]:\tList of (sha, full_message) tuples
\t"""
def get_remotes(cwd: str | None = None) -> dict[str, str]:
    """ Get all git remotes and their URLs.

\tArgs:
\t\tcwd\t(str | None):\tWorking directory for the git command
\tReturns:
\t\tdict[str, str]:\tDictionary mapping remote names to their push URLs
\t"""
def parse_remote_url(remote_url: str) -> tuple[str, str, str] | None:
    ''' Parse a git remote URL to extract hosting info.

\tSupports:
\t- SSH format: git@github.com:user/repo.git
\t- SSH format: git@gitlab.example.com:group/repo.git
\t- HTTPS format: https://github.com/user/repo.git
\t- HTTPS format: https://gitlab.example.com/group/repo.git

\tArgs:
\t\tremote_url\t(str):\tGit remote URL
\tReturns:
\t\ttuple[str, str, str] | None:\t(host_type, base_url, repo_path) or None if cannot parse
\t\t\thost_type:\thost_type: "github", "gitlab", or "unknown"
\t\t\tbase_url:\tBase URL for the repository (e.g., "https://github.com/user/repo")
\t\t\trepo_path:\tRepository path (e.g., "user/repo")

\t>>> parse_remote_url("git@github.com:Stoupy51/stouputils.git")
\t(\'github\', \'https://github.com/Stoupy51/stouputils\', \'Stoupy51/stouputils\')
\t>>> parse_remote_url("https://github.com/Stoupy51/stouputils.git")
\t(\'github\', \'https://github.com/Stoupy51/stouputils\', \'Stoupy51/stouputils\')
\t>>> parse_remote_url("git@gitlab.example.com:group/project.git")
\t(\'gitlab\', \'https://gitlab.example.com/group/project\', \'group/project\')
\t>>> parse_remote_url("https://gitlab.company.com/team/repo.git")
\t(\'gitlab\', \'https://gitlab.company.com/team/repo\', \'team/repo\')
\t>>> parse_remote_url("git@custom-server.com:user/repo.git")
\t(\'gitlab\', \'https://custom-server.com/user/repo\', \'user/repo\')
\t>>> parse_remote_url("invalid-url") is None
\tTrue
\t'''
def detect_host_type(host: str) -> str:
    ''' Detect the type of git hosting service from hostname.

\tArgs:
\t\thost (str):\tHostname (e.g., "github.com", "gitlab.example.com")
\tReturns:
\t\tstr: "github", "gitlab", or "unknown"

\t>>> detect_host_type("github.com")
\t\'github\'
\t>>> detect_host_type("gitlab.com")
\t\'gitlab\'
\t>>> detect_host_type("gitlab.example.com")
\t\'gitlab\'
\t>>> detect_host_type("custom-server.com")
\t\'gitlab\'
\t>>> detect_host_type("my-github-mirror.org")
\t\'github\'
\t'''
def create_url_formatter(remote_url: str) -> tuple[Callable[[str], str], Callable[[str, str], str]] | None:
    """ Create URL formatter functions for a git remote.

\tArgs:
\t\tremote_url\t(str):\tGit remote URL
\tReturns:
\t\ttuple[Callable, Callable] | None:\t(commit_url_formatter, compare_url_formatter) or None
\t"""
def generate_local_changelog(mode: str = 'tag', value: str | None = None, remote: str | None = None, cwd: str | None = None) -> str:
    ''' Generate a changelog from local git history.

\tArgs:
\t\tmode\t(str):\t\t\tMode for selecting commits - "tag", "date", or "commit"
\t\tvalue\t(str | None):\tValue for the mode (tag name, date, or commit SHA).
\t\t\tIf None and mode is "tag", uses the latest tag.
\t\tremote\t(str | None):\tRemote name to use for commit URLs. If None, no URLs are generated.
\t\tcwd\t\t(str | None):\tWorking directory for git commands
\tReturns:
\t\tstr:\tGenerated changelog in Markdown format
\t'''
def changelog_cli() -> None:
    """ CLI interface for generating changelogs from local git history.

\tUsage:
\t\tstouputils changelog [tag|date|commit] [value] [--remote <remote>] [-o <file>]

\tExamples:
\t\tstouputils changelog                          # Uses latest tag (default)
\t\tstouputils changelog tag v1.9.0               # All commits since tag v1.9.0
\t\tstouputils changelog date 2026/01/05          # All commits since date
\t\tstouputils changelog commit 847b27e           # All commits since commit
\t\tstouputils changelog --remote origin          # Use origin remote for commit URLs
\t\tstouputils changelog -o CHANGELOG.md          # Output to file
\t"""
