using ScadaLink.Commons.Entities.Scripts;
using ScadaLink.Commons.Interfaces.Repositories;
using ScadaLink.Commons.Interfaces.Services;
using ScadaLink.Commons.Types;

namespace ScadaLink.TemplateEngine;

/// <summary>
/// WP-5: Shared Script CRUD.
/// System-wide scripts not associated with templates.
/// Same parameter/return definition structure as template scripts.
/// Includes syntax/structural validation (basic C# compilation check).
/// </summary>
public class SharedScriptService
{
    private readonly ITemplateEngineRepository _repository;
    private readonly IAuditService _auditService;

    public SharedScriptService(ITemplateEngineRepository repository, IAuditService auditService)
    {
        _repository = repository ?? throw new ArgumentNullException(nameof(repository));
        _auditService = auditService ?? throw new ArgumentNullException(nameof(auditService));
    }

    public async Task<Result<SharedScript>> CreateSharedScriptAsync(
        string name,
        string code,
        string? parameterDefinitions,
        string? returnDefinition,
        string user,
        CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(name))
            return Result<SharedScript>.Failure("Shared script name is required.");

        if (string.IsNullOrWhiteSpace(code))
            return Result<SharedScript>.Failure("Shared script code is required.");

        // Check unique name
        var existing = await _repository.GetSharedScriptByNameAsync(name, cancellationToken);
        if (existing != null)
            return Result<SharedScript>.Failure($"A shared script named '{name}' already exists.");

        // Syntax/structural validation
        var syntaxError = ValidateSyntax(code);
        if (syntaxError != null)
            return Result<SharedScript>.Failure(syntaxError);

        var script = new SharedScript(name, code)
        {
            ParameterDefinitions = parameterDefinitions,
            ReturnDefinition = returnDefinition
        };

        await _repository.AddSharedScriptAsync(script, cancellationToken);
        await _auditService.LogAsync(user, "Create", "SharedScript", "0", name, script, cancellationToken);
        await _repository.SaveChangesAsync(cancellationToken);

        return Result<SharedScript>.Success(script);
    }

    public async Task<Result<SharedScript>> UpdateSharedScriptAsync(
        int scriptId,
        string code,
        string? parameterDefinitions,
        string? returnDefinition,
        string user,
        CancellationToken cancellationToken = default)
    {
        var script = await _repository.GetSharedScriptByIdAsync(scriptId, cancellationToken);
        if (script == null)
            return Result<SharedScript>.Failure($"Shared script with ID {scriptId} not found.");

        if (string.IsNullOrWhiteSpace(code))
            return Result<SharedScript>.Failure("Shared script code is required.");

        // Syntax/structural validation
        var syntaxError = ValidateSyntax(code);
        if (syntaxError != null)
            return Result<SharedScript>.Failure(syntaxError);

        script.Code = code;
        script.ParameterDefinitions = parameterDefinitions;
        script.ReturnDefinition = returnDefinition;

        await _repository.UpdateSharedScriptAsync(script, cancellationToken);
        await _auditService.LogAsync(user, "Update", "SharedScript", scriptId.ToString(), script.Name, script, cancellationToken);
        await _repository.SaveChangesAsync(cancellationToken);

        return Result<SharedScript>.Success(script);
    }

    public async Task<Result<bool>> DeleteSharedScriptAsync(
        int scriptId,
        string user,
        CancellationToken cancellationToken = default)
    {
        var script = await _repository.GetSharedScriptByIdAsync(scriptId, cancellationToken);
        if (script == null)
            return Result<bool>.Failure($"Shared script with ID {scriptId} not found.");

        await _repository.DeleteSharedScriptAsync(scriptId, cancellationToken);
        await _auditService.LogAsync(user, "Delete", "SharedScript", scriptId.ToString(), script.Name, null, cancellationToken);
        await _repository.SaveChangesAsync(cancellationToken);

        return Result<bool>.Success(true);
    }

    public async Task<SharedScript?> GetSharedScriptByIdAsync(int scriptId, CancellationToken cancellationToken = default)
    {
        return await _repository.GetSharedScriptByIdAsync(scriptId, cancellationToken);
    }

    public async Task<IReadOnlyList<SharedScript>> GetAllSharedScriptsAsync(CancellationToken cancellationToken = default)
    {
        return await _repository.GetAllSharedScriptsAsync(cancellationToken);
    }

    /// <summary>
    /// Basic structural validation of C# script code.
    /// Checks for balanced braces and basic syntax structure.
    /// Full Roslyn compilation would be added in a later phase when the scripting sandbox is available.
    /// </summary>
    internal static string? ValidateSyntax(string code)
    {
        if (string.IsNullOrWhiteSpace(code))
            return "Script code cannot be empty.";

        // Check for balanced braces
        int braceCount = 0;
        int bracketCount = 0;
        int parenCount = 0;

        foreach (var ch in code)
        {
            switch (ch)
            {
                case '{': braceCount++; break;
                case '}': braceCount--; break;
                case '[': bracketCount++; break;
                case ']': bracketCount--; break;
                case '(': parenCount++; break;
                case ')': parenCount--; break;
            }

            if (braceCount < 0)
                return "Syntax error: unmatched closing brace '}'.";
            if (bracketCount < 0)
                return "Syntax error: unmatched closing bracket ']'.";
            if (parenCount < 0)
                return "Syntax error: unmatched closing parenthesis ')'.";
        }

        if (braceCount != 0)
            return "Syntax error: unmatched opening brace '{'.";
        if (bracketCount != 0)
            return "Syntax error: unmatched opening bracket '['.";
        if (parenCount != 0)
            return "Syntax error: unmatched opening parenthesis '('.";

        return null;
    }
}
