from enum import Enum

import pygit2


class HookResult(Enum):
	SUCCESS = 0
	FAILURE = 1


class Hook:
	def __init__(self, repo: pygit2.Repository):
		self.repo = repo


"""
Pre-Commit Hook (Client-side)
Runs before a commit is made, before a commit message is written (if not supplied by -m).
Use this hook to:
- Check for uncommitted changes
- Run tests, lints, security checks, etc.
This can be bypassed with --no-verify by the user.
"""


class PreCommit(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self) -> HookResult:
		return HookResult.SUCCESS


"""
Prepare-Commit-Msg Hook (Client-side)
Runs right after the default log message is prepared and before the editor is started.
Use this hook to:
- Edit the message file in place (e.g. strip template comments)
- Insert a standard prefix/suffix (e.g. branch name, ticket ID)
- Add Signed-off-by from a template
Takes 1–3 parameters: message file path, source (message|template|merge|squash|commit),
and optionally commit hash for amend.
"""


class PrepareCommitMsg(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(
		self,
		message_file: str,
		source: str = "",
		commit_hash: str | None = None,
	) -> HookResult:
		return HookResult.SUCCESS


"""
Commit-Msg Hook (Client-side)
Runs after the commit message is prepared; can be bypassed with --no-verify.
Use this hook to:
- Enforce a project standard format (e.g. conventional commits)
- Validate or normalize the message in place
- Reject the commit (e.g. duplicate Signed-off-by, missing ticket reference)
Takes one parameter: the path to the file holding the proposed commit log message.
"""


class CommitMsg(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self, message_file: str) -> HookResult:
		return HookResult.SUCCESS


"""
Post-Commit Hook (Client-side)
Runs after a commit is made. Cannot affect the outcome of git commit.
Use this hook to:
- Notify (e.g. log, webhook, chat)
- Run post-commit checks or backups
- Update external metadata or caches
"""


class PostCommit(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self) -> HookResult:
		return HookResult.SUCCESS


"""
Pre-Merge-Commit Hook (Client-side)
Runs after a merge has been carried out successfully and before the merge commit
message is finalized; can be bypassed with --no-verify.
Use this hook to:
- Validate the merged tree (e.g. run tests on the result)
- Inspect or adjust the merge commit message
- Abort the merge commit if checks fail
Takes no parameters.
"""


class PreMergeCommit(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self) -> HookResult:
		return HookResult.SUCCESS


"""
Pre-Rebase Hook (Client-side)
Called by git rebase; can be used to prevent a branch from being rebased.
Use this hook to:
- Block rebasing certain branches (e.g. main)
- Run checks before rewriting history
Takes one or two parameters: upstream ref, and optionally the branch being rebased
(absent when rebasing the current branch).
"""


class PreRebase(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self, upstream: str, branch: str | None = None) -> HookResult:
		return HookResult.SUCCESS


"""
Post-Checkout Hook (Client-side)
Runs after git checkout, git switch, or git clone (when a worktree is updated).
Use this hook to:
- Restore working tree metadata (e.g. permissions, ACLs)
- Auto-display differences from the previous HEAD
- Run repository validity checks or refresh generated files
Takes three parameters: previous HEAD ref, new HEAD ref, and a flag (1 = branch checkout, 0 = file checkout).
"""


class PostCheckout(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self, prev_head: str, new_head: str, branch_checkout: str) -> HookResult:
		return HookResult.SUCCESS


"""
Post-Merge Hook (Client-side)
Runs after a successful git merge (e.g. after git pull). Cannot affect the outcome.
Use this hook to:
- Restore working tree metadata in conjunction with pre-commit
- Run post-merge checks or notifications
Takes one parameter: a status flag indicating whether the merge was a squash merge.
"""


class PostMerge(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self, squash: str) -> HookResult:
		return HookResult.SUCCESS


"""
Pre-Push Hook (Client-side)
Called by git push; can be used to prevent a push.
Use this hook to:
- Run tests or lint before pushing
- Enforce branch naming or ref permissions
- Validate commits being pushed
Takes two parameters: remote name and remote URL. Ref updates are provided on stdin.
"""


class PrePush(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self, remote_name: str, remote_url: str) -> HookResult:
		return HookResult.SUCCESS


# -----------------------------------------------------------------------------
# Server-side hooks (run in $GIT_DIR on receive-pack / push)
# -----------------------------------------------------------------------------

"""
Update Hook (Server-side)
Invoked by git-receive-pack once per ref being updated, before the ref is updated.
Use this hook to:
- Enforce fast-forward only (reject non-FF updates)
- Implement per-ref access control
- Log or validate old → new for specific refs
Takes three parameters: ref name, old object name, new object name.
"""


class Update(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self, ref_name: str, old_oid: str, new_oid: str) -> HookResult:
		return HookResult.SUCCESS


"""
Post-Update Hook (Server-side)
Invoked by git-receive-pack once after all refs have been updated.
Use this hook to:
- Notify or trigger CI for updated refs
- Run git update-server-info for dumb transports (e.g. HTTP)
- Update caches or derived data
Takes a variable number of parameters: the name of each ref that was updated.
"""


class PostUpdate(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self, *ref_names: str) -> HookResult:
		return HookResult.SUCCESS


"""
Push-To-Checkout Hook (Server-side)
Invoked when a push updates the currently checked-out branch and receive.denyCurrentBranch is updateInstead.
Use this hook to:
- Override how the working tree and index are updated to match the new commit
- Run git read-tree -u -m to emulate a reverse fetch
- Refuse the push by exiting non-zero (without modifying index or worktree)
Takes one parameter: the commit object name the tip of the current branch will be updated to.
"""


class PushToCheckout(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self, new_commit: str) -> HookResult:
		return HookResult.SUCCESS


"""
Pre-Auto-GC Hook
Invoked by git gc --auto before automatic garbage collection runs.
Use this hook to:
- Prevent or delay gc when the repo is busy (e.g. long-running operations)
- Run housekeeping or consistency checks before gc
- Notify or log that auto-gc is about to run
Takes no parameters. Exiting with non-zero status prevents gc from running.
"""


class PreAutoGc(Hook):
	def __init__(self, repo: pygit2.Repository):
		super().__init__(repo)

	def run(self) -> HookResult:
		return HookResult.SUCCESS


# -----------------------------------------------------------------------------
# Hooks skipped (not implemented in this module)
# -----------------------------------------------------------------------------
#
# E-mail / git-am hooks (skipped by design):
#   - applypatch-msg   (message file; used by git am)
#   - pre-applypatch   (no params; used by git am)
#   - post-applypatch  (no params; used by git am)
#
# Stdin-only or protocol hooks (skipped: no string parameters to pass to run()):
#   - pre-receive         (no args; ref updates on stdin)
#   - post-receive        (no args; ref updates on stdin)
#   - reference-transaction (state string + ref updates on stdin)
#   - proc-receive        (pkt-line protocol on stdin/stdout)