// *****************************************************************************
// Copyright (C) 2026 EclipseSource GmbH.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { nls } from '@theia/core/lib/common/nls';

export const USER_INTERACTION_FUNCTION_ID = 'userInteraction';

export interface EmptyContentRef {
    empty: true;
    label?: string;
}

export interface PathContentRef {
    path: string;
    gitRef?: string;
    line?: number;
}

export type ContentRef = string | PathContentRef | EmptyContentRef;

export function isEmptyContentRef(ref: ContentRef): ref is EmptyContentRef {
    return typeof ref === 'object' && 'empty' in ref && ref.empty === true;
}

export interface UserInteractionLink {
    ref: ContentRef;
    rightRef?: ContentRef;
    label?: string;
    autoOpen?: boolean;
}

export function resolveContentRef(ref: ContentRef): PathContentRef | EmptyContentRef {
    if (typeof ref === 'string') {
        return { path: ref };
    }
    return ref;
}

const SHA_PREFIX_LENGTH = 7;

function workingCopyLabel(): string {
    return nls.localize('theia/ai-ide/userInteractionWorkingCopy', 'Working Copy');
}

function emptyLabel(): string {
    return nls.localize('theia/ai-ide/userInteractionEmpty', 'Empty');
}

function gitRefTag(ref: PathContentRef): string {
    return ref.gitRef ? ref.gitRef.substring(0, SHA_PREFIX_LENGTH) : workingCopyLabel();
}

export function buildDiffLabel(
    left: PathContentRef | EmptyContentRef,
    right: PathContentRef | EmptyContentRef
): string {
    const leftIsEmpty = isEmptyContentRef(left);
    const rightIsEmpty = isEmptyContentRef(right);
    if (leftIsEmpty && rightIsEmpty) {
        return `${left.label || emptyLabel()} ⟷ ${right.label || emptyLabel()}`;
    }
    if (leftIsEmpty) {
        return `${(right as PathContentRef).path} (${left.label || emptyLabel()} ⟷ ${gitRefTag(right as PathContentRef)})`;
    }
    if (rightIsEmpty) {
        return `${(left as PathContentRef).path} (${gitRefTag(left as PathContentRef)} ⟷ ${right.label || emptyLabel()})`;
    }
    const leftPath = left as PathContentRef;
    const rightPath = right as PathContentRef;
    if (leftPath.path === rightPath.path) {
        return `${leftPath.path} (${gitRefTag(leftPath)} ⟷ ${gitRefTag(rightPath)})`;
    }
    return `${leftPath.path} ⟷ ${rightPath.path}`;
}

export interface UserInteractionOption {
    text: string;
    value: string;
    description?: string;
    buttonLabel?: string;
}

export interface UserInteractionStep {
    title: string;
    message: string;
    options?: UserInteractionOption[];
    links?: UserInteractionLink[];
}

export interface UserInteractionStepResult {
    title: string;
    value?: string;
    comments?: string[];
    skipped?: boolean;
}

export interface UserInteractionResult {
    completed: boolean;
    steps: UserInteractionStepResult[];
}

export interface UserInteractionArgs {
    interactions: UserInteractionStep[];
}

export interface UserInteractionInput {
    title: string;
    stepCount: number;
}

export function parseUserInteractionInput(args: string | undefined): UserInteractionInput {
    if (!args) {
        return { title: '', stepCount: 0 };
    }
    try {
        const parsed = JSON.parse(args);
        const interactions = Array.isArray(parsed.interactions) ? parsed.interactions : [];
        const firstTitle = typeof interactions[0]?.title === 'string' ? interactions[0].title : '';
        return { title: firstTitle, stepCount: interactions.length };
    } catch {
        const match = /"title"\s*:\s*"([^"]*)"?/.exec(args);
        return { title: match?.[1] ?? '', stepCount: 0 };
    }
}

export function parseUserInteractionResult(raw: unknown): UserInteractionResult | undefined {
    let candidate: unknown = raw;
    if (typeof raw === 'string') {
        try {
            candidate = JSON.parse(raw);
        } catch {
            return undefined;
        }
    }
    if (!candidate || typeof candidate !== 'object') {
        return undefined;
    }
    const obj = candidate as Record<string, unknown>;
    if (typeof obj.completed !== 'boolean' || !Array.isArray(obj.steps)) {
        return undefined;
    }
    return obj as unknown as UserInteractionResult;
}

export function parseUserInteractionArgs(args: string | undefined): UserInteractionArgs | undefined {
    if (!args) {
        return undefined;
    }
    try {
        const parsed = JSON.parse(args);
        if (!Array.isArray(parsed.interactions)) {
            return undefined;
        }
        const validSteps = parsed.interactions
            .map(parseStep)
            .filter((step: UserInteractionStep | undefined): step is UserInteractionStep => step !== undefined);
        if (validSteps.length === 0) {
            return undefined;
        }
        return { interactions: validSteps };
    } catch {
        return undefined;
    }
}

function parseStep(raw: unknown): UserInteractionStep | undefined {
    if (!raw || typeof raw !== 'object') {
        return undefined;
    }
    const obj = raw as Record<string, unknown>;
    if (typeof obj.title !== 'string' || typeof obj.message !== 'string') {
        return undefined;
    }
    let options: UserInteractionOption[] | undefined;
    if (Array.isArray(obj.options)) {
        const validOptions = obj.options.filter(
            (opt: unknown) => !!opt && typeof opt === 'object'
                && typeof (opt as Record<string, unknown>).text === 'string'
                && typeof (opt as Record<string, unknown>).value === 'string'
        ) as UserInteractionOption[];
        options = validOptions.length > 0 ? validOptions : undefined;
    }
    let links: UserInteractionLink[] | undefined;
    if (Array.isArray(obj.links)) {
        const filtered = obj.links
            .map(normalizeUserInteractionLink)
            .filter((link: UserInteractionLink | undefined): link is UserInteractionLink => link !== undefined);
        links = filtered.length > 0 ? filtered : undefined;
    } else {
        const link = normalizeUserInteractionLink(obj.link);
        if (link) {
            links = [link];
        }
    }
    return {
        title: obj.title,
        message: obj.message,
        options,
        links
    };
}

function normalizeContentRef(ref: unknown): ContentRef | undefined {
    if (typeof ref === 'string') {
        return ref.length > 0 ? ref : undefined;
    }
    if (ref && typeof ref === 'object') {
        const obj = ref as Record<string, unknown>;
        if (obj.empty === true) {
            const empty: EmptyContentRef = { empty: true };
            if (typeof obj.label === 'string' && obj.label.length > 0) {
                empty.label = obj.label;
            }
            return empty;
        }
        if (typeof obj.path === 'string' && obj.path.length > 0) {
            const pathRef: PathContentRef = { path: obj.path };
            if (typeof obj.gitRef === 'string' && obj.gitRef.length > 0) {
                pathRef.gitRef = obj.gitRef;
            }
            if (typeof obj.line === 'number' && Number.isFinite(obj.line) && obj.line > 0) {
                pathRef.line = obj.line;
            }
            return pathRef;
        }
    }
    return undefined;
}

export function normalizeUserInteractionLink(link: unknown): UserInteractionLink | undefined {
    if (!link || typeof link !== 'object') {
        return undefined;
    }
    const obj = link as Record<string, unknown>;
    const ref = normalizeContentRef(obj.ref);
    if (!ref) {
        return undefined;
    }
    const normalized: UserInteractionLink = { ref };
    const rightRef = normalizeContentRef(obj.rightRef);
    if (rightRef) {
        normalized.rightRef = rightRef;
    }
    if (typeof obj.label === 'string' && obj.label.length > 0) {
        normalized.label = obj.label;
    }
    if (typeof obj.autoOpen === 'boolean') {
        normalized.autoOpen = obj.autoOpen;
    }
    return normalized;
}
