/**
 * Template provider for scaffolding new Acurity files.
 * Offers modern conventions and best practices for reports and include files.
 */

import * as vscode from "vscode";
import { LANGUAGE_ID } from "../../core/src/constants.js";
import { log } from "../../core/src/logger.js";
import {
    getExpectedRobodocHeader,
    resolveAuthorName,
    getBaseFilename
} from "../../core/src/robodoc/robodocUtils.js";

/**
 * Reads ROBODOC configuration from VS Code workspace settings.
 * Lives in the extension layer so that core/robodocUtils.js stays free of vscode dependencies.
 */
function readVscodeRobodocConfig() {
    const config = vscode.workspace.getConfiguration("acurity.robodoc");
    return {
        enabled:               config.get("enabled",               true),
        requireDocBlock:       config.get("requireDocBlock",       true),
        requiredSections:      config.get("requiredSections",      ["DESCRIPTION", "CHANGE HISTORY", "USES"]),
        reportFilenamePattern: config.get("reportFilenamePattern", "^[A-Za-z0-9]{4}\\.TXT$"),
        reportHeaderPrefix:    config.get("reportHeaderPrefix",    "REPORT/"),
        includeHeaderPrefix:   config.get("includeHeaderPrefix",   "INCLUDE/"),
        validateHeaderTarget:  config.get("validateHeaderTarget",  true),
        authorName:            config.get("authorName",            "")
    };
}

const TEMPLATE_PREFERENCE_KEY = "acurity.templates.dontAskAgain";
const AUTHOR_PROMPT_SHOWN_KEY = "acurity.templates.authorPromptShown";

function getTodayIsoDate() {
    return new Date().toISOString().split("T")[0];
}

function buildReportTemplate({ reportName, authorName, robodocTarget, today }) {
    return `<HTML>
#HASH-OFF
//@docStart*r* ${robodocTarget}
//
// ${reportName} - Report description
//
// DESCRIPTION
// Detailed description of what this report does and its purpose.
//
// PARAMETERS
// RLz_Report_Parms : Parameter description (e.g., 'UPDATE' to commit changes)
// RLd_Start        : Start date for report period
// RLd_End          : End date for report period
// RLz_Fund         : Fund code (e.g., 'ABCD' or '****' for all funds)
// RLz_Member       : Member code (e.g., '123456' or '******' for all members)
//
// CHANGE HISTORY
// ${today}          - ${authorName}      : Initial version
//
// USES
// This report uses the following include files:
// (Add INCLUDE or INCLUDEONCE statements here as needed)
//
//@docEnd

// ==================
// VARIABLE DECLARATIONS
// ==================
VAR zFund, zMember              : STRING
VAR dStartDate, dEndDate        : DATE
VAR lRowCount                   : LONG

// Initialize variables from report parameters
SET dStartDate = hRLd_Start
SET dEndDate   = hRLd_End
SET zFund      = hRLz_Fund
SET zMember    = hRLz_Member
SET lRowCount  = 0

// ==================
// HTML HEADER
// ==================
>><html>
>><head>
>>  <title>${reportName}</title>
>>  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
>></head>
>><body>
>><div class="container">
>>  <h1>${reportName}</h1>

// ==================
// MAIN REPORT LOGIC
// ==================

// Add your report logic here

// Display row count
>>  <p>Total rows processed: ^lRowCount^</p>

// ==================
// HTML FOOTER
// ==================
>></div>
>></body>
>></html>
`;
}

function buildIncludeTemplate({ includeName, authorName, robodocTarget, today }) {
    return `#HASH-OFF
//@docStart*h* ${robodocTarget}
//
// DESCRIPTION
// Include file description and responsibilities.
//
// CHANGE HISTORY
// ${today}          - ${authorName}      : Initial version
//
// USES
// This include uses the following include files:
// (Add INCLUDEONCE statements here as needed)
//
// SNIPPET
// Add exported constants and functions below.
//@docEnd

// ==================
// CONSTANTS / GLOBALS
// ==================
VAR gs${includeName}_VERSION : STRING
SET gs${includeName}_VERSION = "1.0"

// ==================
// PUBLIC FUNCTIONS
// ==================
FUNCTION ${includeName}_Example():STRING
    RETURN ""
ENDFUNCTION
`;
}

function buildReportSnippet({ reportName, authorName, robodocTarget, today }) {
    const s = new vscode.SnippetString();
    s.appendText(`<HTML>\n#HASH-OFF\n//@docStart*r* ${robodocTarget}\n//\n// ${reportName} - `);
    s.appendPlaceholder("Report description", 1);
    s.appendText(`\n//\n// DESCRIPTION\n// `);
    s.appendPlaceholder("Detailed description of what this report does and its purpose.", 2);
    s.appendText(
        `\n//\n// PARAMETERS\n` +
        `// RLz_Report_Parms : Parameter description (e.g., 'UPDATE' to commit changes)\n` +
        `// RLd_Start        : Start date for report period\n` +
        `// RLd_End          : End date for report period\n` +
        `// RLz_Fund         : Fund code (e.g., 'ABCD' or '****' for all funds)\n` +
        `// RLz_Member       : Member code (e.g., '123456' or '******' for all members)\n` +
        `//\n// CHANGE HISTORY\n` +
        `// ${today}          - ${authorName}      : Initial version\n` +
        `//\n// USES\n// This report uses the following include files:\n` +
        `// (Add INCLUDE or INCLUDEONCE statements here as needed)\n` +
        `//\n//@docEnd\n\n` +
        `// ==================\n// VARIABLE DECLARATIONS\n// ==================\n` +
        `VAR zFund, zMember              : STRING\n` +
        `VAR dStartDate, dEndDate        : DATE\n` +
        `VAR lRowCount                   : LONG\n\n` +
        `// Initialize variables from report parameters\n` +
        `SET dStartDate = hRLd_Start\n` +
        `SET dEndDate   = hRLd_End\n` +
        `SET zFund      = hRLz_Fund\n` +
        `SET zMember    = hRLz_Member\n` +
        `SET lRowCount  = 0\n\n` +
        `// ==================\n// HTML HEADER\n// ==================\n` +
        `>><html>\n>><head>\n` +
        `>>  <title>${reportName}</title>\n` +
        `>>  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">\n` +
        `>></head>\n>><body>\n>><div class="container">\n` +
        `>>  <h1>${reportName}</h1>\n\n` +
        `// ==================\n// MAIN REPORT LOGIC\n// ==================\n`
    );
    s.appendTabstop(0);
    s.appendText(
        `\n\n// Display row count\n` +
        `>>  <p>Total rows processed: ^lRowCount^</p>\n\n` +
        `// ==================\n// HTML FOOTER\n// ==================\n` +
        `>></div>\n>></body>\n>></html>\n`
    );
    return s;
}

function buildIncludeSnippet({ includeName, authorName, robodocTarget, today }) {
    const s = new vscode.SnippetString();
    s.appendText(`#HASH-OFF\n//@docStart*h* ${robodocTarget}\n//\n// DESCRIPTION\n// `);
    s.appendPlaceholder("Include file description and responsibilities.", 1);
    s.appendText(
        `\n//\n// CHANGE HISTORY\n` +
        `// ${today}          - ${authorName}      : Initial version\n` +
        `//\n// USES\n// This include uses the following include files:\n` +
        `// (Add INCLUDEONCE statements here as needed)\n` +
        `//\n// SNIPPET\n// Add exported constants and functions below.\n` +
        `//@docEnd\n\n` +
        `// ==================\n// CONSTANTS / GLOBALS\n// ==================\n` +
        `VAR gs${includeName}_VERSION : STRING\n` +
        `SET gs${includeName}_VERSION = "1.0"\n\n` +
        `// ==================\n// PUBLIC FUNCTIONS\n// ==================\n`
    );
    s.appendTabstop(0);
    s.appendText(`\n`);
    return s;
}

function buildSnippetForFilename(filename, options = {}) {
    const robodocConfig = options.robodocConfig || readVscodeRobodocConfig();
    const authorName = options.authorName || "[Your Name]";
    const today = options.today || getTodayIsoDate();
    const baseFilename = filename || "NEW_REPORT.TXT";
    const expectedHeader = getExpectedRobodocHeader(baseFilename, robodocConfig);

    if (expectedHeader.kind === "report") {
        const reportName = baseFilename.replace(/\.[^.]+$/, "");
        return buildReportSnippet({ reportName, authorName, robodocTarget: expectedHeader.target, today });
    }

    const includeName = baseFilename.replace(/\.[^.]+$/, "");
    return buildIncludeSnippet({ includeName, authorName, robodocTarget: expectedHeader.target, today });
}

function generateTemplateForFilename(filename, options = {}) {
    const robodocConfig = options.robodocConfig || readVscodeRobodocConfig();
    const authorName = options.authorName || "[Your Name]";
    const today = options.today || getTodayIsoDate();
    const baseFilename = filename || "NEW_REPORT.TXT";
    const expectedHeader = getExpectedRobodocHeader(baseFilename, robodocConfig);

    if (expectedHeader.kind === "report") {
        const reportName = baseFilename.replace(/\.[^.]+$/, "");
        return buildReportTemplate({
            reportName,
            authorName,
            robodocTarget: expectedHeader.target,
            today
        });
    }

    const includeName = baseFilename.replace(/\.[^.]+$/, "");
    return buildIncludeTemplate({
        includeName,
        authorName,
        robodocTarget: expectedHeader.target,
        today
    });
}

/**
 * Generates a modern Acurity report template with best practices.
 * @param {string} filename - The filename for context (e.g., "MY_REPORT.TXT")
 * @param {Object} options - Optional generation options
 * @returns {string} The template content
 */
function generateReportTemplate(filename, options = {}) {
    const robodocConfig = {
        ...readVscodeRobodocConfig(),
        reportFilenamePattern: ".*"
    };
    return generateTemplateForFilename(filename || "NEW_REPORT.TXT", {
        ...options,
        robodocConfig
    });
}

/**
 * Returns true when the file is inside a syntax-tests directory (within 2
 * sub-directory levels).  Matches paths like:
 *   …/syntax-tests/foo.TXT                     (0 levels deep)
 *   …/syntax-tests/core/foo.TXT                (1 level deep)
 *   …/syntax-tests/core/index-functions/foo.TXT (2 levels deep)
 *
 * @param {vscode.TextDocument} document
 * @returns {boolean}
 */
function isSyntaxTestFile(document) {
    const normalized = document.uri.fsPath.replace(/\\/g, "/");
    const markerIdx = normalized.lastIndexOf("/syntax-tests/");
    if (markerIdx === -1) return false;
    const afterMarker = normalized.slice(markerIdx + "/syntax-tests/".length);
    // Count path separators: 0 separators = file directly in syntax-tests,
    // 1 = one subfolder, 2 = two subfolders.  Strip the filename (last segment).
    const parts = afterMarker.split("/");
    return parts.length - 1 <= 2; // parts.length - 1 = number of directory levels
}

function buildSyntaxTestSnippet(filename) {
    const base = filename ? filename.replace(/\.[^.]+$/, "") : "my-test";
    const s = new vscode.SnippetString();
    s.appendText(
        `#HASH-OFF\nNEWINTERPRETER\n//\n// `
    );
    s.appendPlaceholder("The purpose of this test is to...", 1);
    s.appendText(
        `\n//\n` +
        `// Test applies to: **strict** + **legacy** + **default** modes` +
        ` ( as defined in ${base}.test-spec.json ).\n` +
        `// To limit extraneous warnings, we shall write our test in strict mode:\n` +
        `// @mode:strict\n//\n` +
        `// TEST SETUP\n// ==========\nSTRING zTestString\n\n\n` +
        `// ACCEPTED FORMATS\n// ==================\n`
    );
    s.appendTabstop(2);
    s.appendText(`\n\n\n// REJECTED FORMATS\n// ==================\n`);
    s.appendTabstop(0);
    s.appendText(`\n`);
    return s;
}

/**
 * Checks if a document is blank or contains only whitespace.
 * @param {vscode.TextDocument} document - The document to check
 * @returns {boolean} True if the document is effectively blank
 */
function isBlankDocument(document) {
    if (document.lineCount === 0) {
        return true;
    }
    
    // Check if all lines are empty or whitespace-only
    const text = document.getText().trim();
    return text.length === 0;
}

/**
 * Checks user preference for template insertion prompts.
 * @param {vscode.ExtensionContext} context - The extension context
 * @returns {boolean} True if user has opted out of prompts
 */
function shouldSkipPrompt(context) {
    const promptEnabled = vscode.workspace.getConfiguration("acurity.templates").get("promptOnBlankFile", true);
    if (!promptEnabled) {
        return true;
    }
    return context.globalState.get(TEMPLATE_PREFERENCE_KEY, false);
}

/**
 * Sets user preference to not show template prompts again.
 * @param {vscode.ExtensionContext} context - The extension context
 */
async function setDontAskAgain(context) {
    await context.globalState.update(TEMPLATE_PREFERENCE_KEY, true);
    log("User opted out of template insertion prompts");
}

/**
 * Resets user preference for template prompts (useful for testing/debugging).
 * @param {vscode.ExtensionContext} context - The extension context
 */
async function resetTemplatePreference(context) {
    await context.globalState.update(TEMPLATE_PREFERENCE_KEY, false);
    log("Template prompt preference reset");
}

/**
 * Offers a one-time gentle prompt to configure ROBODOC author name.
 * @param {vscode.ExtensionContext} context - The extension context
 */
async function maybePromptForAuthorName(context) {
    const robodocConfig = readVscodeRobodocConfig();
    if (robodocConfig.authorName && robodocConfig.authorName.trim()) {
        return;
    }

    const alreadyPrompted = context.globalState.get(AUTHOR_PROMPT_SHOWN_KEY, false);
    if (alreadyPrompted) {
        return;
    }

    await context.globalState.update(AUTHOR_PROMPT_SHOWN_KEY, true);

    const selection = await vscode.window.showInformationMessage(
        "Set 'acurity.robodoc.authorName' to auto-fill ROBODOC change history author.",
        "Set Author Name",
        "Not Now"
    );

    if (selection !== "Set Author Name") {
        return;
    }

    const input = await vscode.window.showInputBox({
        title: "Acurity ROBODOC Author Name",
        prompt: "Enter the author name to use in generated ROBODOC templates",
        placeHolder: "e.g. John Smith",
        ignoreFocusOut: true
    });

    const trimmed = (input || "").trim();
    if (!trimmed) {
        vscode.window.showInformationMessage("ROBODOC author name was not set. You can configure it later in Settings.");
        return;
    }

    await vscode.workspace.getConfiguration("acurity.robodoc").update(
        "authorName",
        trimmed,
        vscode.ConfigurationTarget.Global
    );
    vscode.window.showInformationMessage(`ROBODOC author name set to '${trimmed}'.`);
}

/**
 * Prompts user to insert template content and handles the insertion.
 * @param {vscode.TextEditor} editor - The active text editor
 * @param {vscode.ExtensionContext} context - The extension context
 */
async function promptForTemplateInsertion(editor, context) {
    const document = editor.document;
    
    // Skip if user has opted out
    if (shouldSkipPrompt(context)) {
        return;
    }
    
    // Skip if not blank
    if (!isBlankDocument(document)) {
        return;
    }
    
    // Syntax test files get their own template prompt
    if (isSyntaxTestFile(document)) {
        const testSelection = await vscode.window.showInformationMessage(
            "Would you like to populate this file with an Acurity Syntax-Test template?",
            "Insert Template",
            "Not Now"
        );
        if (testSelection === "Insert Template") {
            const snippet = buildSyntaxTestSnippet(getBaseFilename(document));
            await editor.insertSnippet(snippet, new vscode.Position(0, 0));
            log(`Syntax-test template inserted into ${getBaseFilename(document)}`);
        }
        return;
    }

    const filename = getBaseFilename(document);
    const expectedHeader = getExpectedRobodocHeader(filename, readVscodeRobodocConfig());
    const inferredKind = expectedHeader.kind.toUpperCase();

    // Show notification with options
    const selection = await vscode.window.showInformationMessage(
        `Would you like to populate this file with a modern Acurity ${inferredKind} template?`,
        "Insert Template",
        "Not Now",
        "Don't Ask Again"
    );
    
    if (selection === "Insert Template") {
        await maybePromptForAuthorName(context);

        const snippet = buildSnippetForFilename(filename, {
            authorName: resolveAuthorName(document, readVscodeRobodocConfig())
        });

        await editor.insertSnippet(snippet, new vscode.Position(0, 0));
        log(`Template inserted into ${filename}`);
    } else if (selection === "Don't Ask Again") {
        await setDontAskAgain(context);
        vscode.window.showInformationMessage("You can re-enable template prompts via the Command Palette: 'Acurity: Reset Template Preference'");
    }
}

/**
 * Initializes the template provider and registers event handlers.
 * @param {vscode.ExtensionContext} context - The extension context
 */
function initializeTemplateProvider(context) {
    log("Initializing template provider");
    
    // Register command to reset template preferences
    context.subscriptions.push(
        vscode.commands.registerCommand("acurity.resetTemplatePreference", async () => {
            await resetTemplatePreference(context);
            vscode.window.showInformationMessage("Template prompt preference has been reset. You will be prompted again for blank files.");
        })
    );
    
    // Register command to manually insert template
    context.subscriptions.push(
        vscode.commands.registerCommand("acurity.insertTemplate", async () => {
            const editor = vscode.window.activeTextEditor;
            if (!editor) {
                vscode.window.showWarningMessage("No active editor found.");
                return;
            }
            
            if (editor.document.languageId !== LANGUAGE_ID) {
                vscode.window.showWarningMessage("Current file is not an Acurity file (.TXT).");
                return;
            }
            
            const filename = getBaseFilename(editor.document);
            await maybePromptForAuthorName(context);
            const robodocConfig = readVscodeRobodocConfig();
            const snippet = buildSnippetForFilename(filename, {
                authorName: resolveAuthorName(editor.document, robodocConfig)
            });

            await editor.insertSnippet(snippet);
            log(`Template inserted into ${filename}`);
        })
    );

    context.subscriptions.push(
        vscode.commands.registerCommand("acurity.openTemplateSettings", async () => {
            await vscode.commands.executeCommand("workbench.action.openSettings", "acurity.robodoc");
        })
    );
    
    // Monitor when Acurity documents are opened
    context.subscriptions.push(
        vscode.workspace.onDidOpenTextDocument(async (document) => {
            if (document.languageId !== LANGUAGE_ID) {
                return;
            }
            
            // Small delay to ensure editor is ready
            setTimeout(async () => {
                const editor = vscode.window.activeTextEditor;
                if (editor && editor.document === document) {
                    await promptForTemplateInsertion(editor, context);
                }
            }, 300);
        })
    );
    
    // Also check currently active document on activation (in case extension loads after file is open)
    const activeEditor = vscode.window.activeTextEditor;
    if (activeEditor && activeEditor.document.languageId === LANGUAGE_ID) {
        setTimeout(async () => {
            await promptForTemplateInsertion(activeEditor, context);
        }, 500);
    }
    
    log("Template provider initialized");
}

export {
    initializeTemplateProvider,
    generateReportTemplate,
    generateTemplateForFilename,
    isBlankDocument
};
