import type { GitFileConflictStatus } from '@gitlens/git/models/fileStatus.js';
import type { ConflictDetectionResult } from '@gitlens/git/models/mergeConflicts.js';
import type {
	ProcessedRebaseCommitEntry as _RebaseCommitEntry,
	ProcessedRebaseCommandEntry,
	ProcessedRebaseCommitEntry,
	RebaseTodoCommitAction,
} from '@gitlens/git/models/rebase.js';
import type { Config } from '../../config.js';
import type { Subscription } from '../../plus/gk/models/subscription.js';
import type { WebviewItemContext } from '../../system/webview.js';
import type { IpcScope } from '../ipc/models/ipc.js';
import { IpcCommand, IpcNotification, IpcRequest } from '../ipc/models/ipc.js';
import type { WebviewState } from '../protocol.js';

export const scope: IpcScope = 'rebase';

export interface State extends WebviewState<'gitlens.rebase'> {
	branch: string;
	onto: { sha: string; commit?: Commit } | undefined;

	/** True if the commits are already on top of onto */
	isInPlace: boolean;

	/** Pending entries that can still be edited */
	entries: RebaseEntry[];
	/** Entries that have already been applied (only present during active rebase) */
	doneEntries?: RebaseEntry[];
	authors: Record<string, Author>;

	ascending: boolean;

	/**
	 * True if this is a --rebase-merges rebase with actual merge commits.
	 * Reordering is disabled to preserve the DAG structure, but action changes are allowed.
	 */
	preservesMerges?: boolean;

	/** Layout density */
	density: Config['rebaseEditor']['density'];
	/** Where to reveal commits when clicking on links or double-clicking rows */
	revealLocation: Config['rebaseEditor']['revealLocation'];
	/** When to automatically reveal commits */
	revealBehavior: Config['rebaseEditor']['revealBehavior'];

	/** Active rebase status - undefined if starting a new rebase */
	rebaseStatus?: RebaseActiveStatus;

	/** Repository path for the rebase */
	repoPath: string;

	/** Subscription state for Pro feature gating */
	subscription?: Subscription;

	/** Conflicted files when rebase is paused due to conflicts */
	conflictFiles?: ConflictFileInfo[];

	/** Whether the close-warning banner has been dismissed */
	closeWarningDismissed?: boolean;

	/** Whether AI features are allowed (user-enabled and org-permitted) — gates the Resolve Conflicts action */
	aiAllowed?: boolean;
}

export interface ConflictFileInfo {
	path: string;
	conflictStatus: GitFileConflictStatus;
	/** Number of conflict markers in the file */
	conflictCount?: number;
}

export interface ConflictFileContextValue {
	type: 'rebaseConflict';
	path: string;
	conflictStatus: GitFileConflictStatus;
}

export type ConflictFileWebviewContext = WebviewItemContext<ConflictFileContextValue>;

/** Reason the rebase is paused */
export type RebasePauseReason = 'edit' | 'reword' | 'break' | 'conflict' | 'exec';

/** Status information for an active (in-progress) rebase */
export interface RebaseActiveStatus {
	/** Current step number (1-based) - from Git's rebase progress */
	currentStep: number;
	/** Total number of steps - from Git's rebase progress */
	totalSteps: number;
	/** SHA of commit currently being processed (REBASE_HEAD) */
	currentCommit?: string;
	/** True if there are conflicts to resolve */
	hasConflicts?: boolean;
	isPaused: boolean;
	/** Reason the rebase is paused (undefined if not paused/in progress) */
	pauseReason?: RebasePauseReason;
}

export interface RebaseCommandEntry extends ProcessedRebaseCommandEntry {
	commit?: never;
}
/** Commit-based rebase entry (pick, reword, edit, squash, fixup, drop) */
export interface RebaseCommitEntry extends ProcessedRebaseCommitEntry {
	commit?: Commit;
}

export type RebaseEntry = RebaseCommitEntry | RebaseCommandEntry;

/** Checks if an entry is a commit entry */
export function isCommitEntry(entry: RebaseEntry): entry is RebaseCommitEntry {
	return entry.type === 'commit';
}

/** Checks if an entry is a command entry */
export function isCommandEntry(entry: RebaseEntry): entry is ProcessedRebaseCommandEntry {
	return entry.type === 'command';
}

export interface Author {
	readonly author: string;
	avatarUrl: string | undefined;
	readonly avatarFallbackUrl?: string | undefined;
	readonly email: string | undefined;
}

export interface Commit {
	readonly sha: string;
	readonly author: string;
	readonly committer: string;
	readonly date: string;
	readonly formattedDate: string;
	readonly message: string;
}

// COMMANDS

export const AbortCommand = new IpcCommand(scope, 'abort');
export const ContinueCommand = new IpcCommand(scope, 'continue');
export const SearchCommand = new IpcCommand(scope, 'search');
export const SkipCommand = new IpcCommand(scope, 'skip');
export const StartCommand = new IpcCommand(scope, 'start');
export const SwitchCommand = new IpcCommand(scope, 'switch');

export interface ReorderParams {
	ascending: boolean;
}
export const ReorderCommand = new IpcCommand<ReorderParams>(scope, 'reorder');

export interface ChangeEntryParams {
	sha: string;
	action: RebaseTodoCommitAction;
}
export const ChangeEntryCommand = new IpcCommand<ChangeEntryParams>(scope, 'change/entry');

export interface ChangeEntriesParams {
	entries: { sha: string; action: RebaseTodoCommitAction }[];
}
export const ChangeEntriesCommand = new IpcCommand<ChangeEntriesParams>(scope, 'change/entries');

export interface MoveEntryParams {
	/** Entry identifier - sha for commits, line number for command entries */
	id: string;
	to: number;
	relative: boolean;
}
export const MoveEntryCommand = new IpcCommand<MoveEntryParams>(scope, 'move/entry');

export interface MoveEntriesParams {
	/** Entry identifiers - sha for commits, line number for command entries */
	ids: string[];
	to: number;
}
export const MoveEntriesCommand = new IpcCommand<MoveEntriesParams>(scope, 'move/entries');

export interface ShiftEntriesParams {
	/** Entry identifiers - sha for commits, line number for command entries */
	ids: string[];
	direction: 'up' | 'down';
}
export const ShiftEntriesCommand = new IpcCommand<ShiftEntriesParams>(scope, 'shift/entries');

export interface UpdateSelectionParams {
	sha: string;
}
export const UpdateSelectionCommand = new IpcCommand<UpdateSelectionParams>(scope, 'selection/update');

export interface RevealRefParams {
	type: 'branch' | 'commit';
	ref: string;
}
export const RevealRefCommand = new IpcCommand<RevealRefParams>(scope, 'revealRef');

// Avatar commands - similar to Graph's missing avatars pattern
export interface GetMissingAvatarsParams {
	/** Map of email → sha for commits that need avatar fetching */
	emails: Record<string, string>;
}
export const GetMissingAvatarsCommand = new IpcCommand<GetMissingAvatarsParams>(scope, 'avatars/get');

// Commit enrichment commands - on-demand loading pattern
export interface GetMissingCommitsParams {
	/** Array of commit SHAs that need enrichment */
	shas: string[];
}
export const GetMissingCommitsCommand = new IpcCommand<GetMissingCommitsParams>(scope, 'commits/get');

export const RecomposeCommand = new IpcCommand(scope, 'recompose/open');
export const DismissCloseWarningCommand = new IpcCommand(scope, 'closeWarning/dismiss');

export interface OpenConflictFileParams {
	path: string;
}
export const OpenConflictFileCommand = new IpcCommand<OpenConflictFileParams>(scope, 'conflicts/openFile');

export interface OpenConflictChangesParams {
	path: string;
	side: 'current' | 'incoming';
}
export const OpenConflictChangesCommand = new IpcCommand<OpenConflictChangesParams>(scope, 'conflicts/openChanges');

export interface ResolveConflictParams {
	path: string;
	resolution: 'current' | 'incoming';
}
export const ResolveConflictCommand = new IpcCommand<ResolveConflictParams>(scope, 'conflicts/resolve');

export interface StageConflictParams {
	path: string;
}
export const StageConflictCommand = new IpcCommand<StageConflictParams>(scope, 'conflicts/stage');

export interface ResolveAllConflictsParams {
	resolution: 'current' | 'incoming';
}
export const ResolveAllConflictsCommand = new IpcCommand<ResolveAllConflictsParams>(scope, 'conflicts/resolveAll');

export const ResolveConflictsInGraphCommand = new IpcCommand(scope, 'conflicts/resolveInGraph');

// REQUESTS

export interface GetConflictsParams {
	/** Distinguishes initial (on-load / upgrade) checks from dynamic (plan-modification / rebase-advance) checks */
	trigger: 'initial' | 'todo';
	/** The onto target SHA */
	onto: string;
	/** Commit SHAs to check for conflicts, in plan order */
	commits: string[];
	/** Optional base override (e.g. 'HEAD' during an active rebase). Defaults to `onto`. */
	base?: string;
	/** Only honored when `trigger === 'initial'`. */
	stopOnFirstConflict?: boolean;
}
export interface DidGetConflictsParams {
	conflicts?: ConflictDetectionResult;
}
export const GetConflictsRequest = new IpcRequest<GetConflictsParams, DidGetConflictsParams>(scope, 'conflicts/get');

// NOTIFICATIONS

export interface DidChangeParams {
	state: State;
}
export const DidChangeNotification = new IpcNotification<DidChangeParams>(scope, 'didChange');

export interface DidChangeAvatarsParams {
	/** Map of author name → avatar URL */
	avatars: Record<string, string>;
}
export const DidChangeAvatarsNotification = new IpcNotification<DidChangeAvatarsParams>(scope, 'avatars/didChange');

export interface DidChangeCommitsParams {
	/** Map of commit SHA → enriched commit data */
	commits: Record<string, Commit>;
	/** Map of author name → author info (for new authors from fetched commits) */
	authors: Record<string, Author>;
	/** True if the commits are already on top of onto (recalculated when commits are enriched) */
	isInPlace?: boolean;
}
export const DidChangeCommitsNotification = new IpcNotification<DidChangeCommitsParams>(scope, 'commits/didChange');

export interface DidChangeSubscriptionParams {
	subscription: Subscription;
}
export const DidChangeSubscriptionNotification = new IpcNotification<DidChangeSubscriptionParams>(
	scope,
	'subscription/didChange',
);
