using System.Globalization;
using Inspectron.Epson.PrintServer.Printers.Utils;
using Inspectron.Epson.Templates.Configuration;
using Inspectron.Epson.Templates.Interpreter;
using Inspectron.Epson.Templates.Language;
using Inspectron.Epson.Templates.Language.Nodes;
using Inspectron.Epson.Templates.Storage;
using Inspectron.Epson.Templates.Validation;

namespace Inspectron.Epson.Templates;

public class TemplateEngine
{
    private readonly ITemplateStorage _storage;
    private readonly PrinterProfileRegistry _profileRegistry;
    private readonly TemplateResolver _resolver;
    private readonly CultureInfo? _culture;

    public TemplateEngine(
        ITemplateStorage storage,
        PrinterProfileRegistry profileRegistry,
        TemplateResolver resolver,
        CultureInfo? culture = null)
    {
        _storage = storage ?? throw new ArgumentNullException(nameof(storage));
        _profileRegistry = profileRegistry ?? throw new ArgumentNullException(nameof(profileRegistry));
        _resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));
        _culture = culture;
    }

    public List<PrintCommand> Render(int receiptType, byte printerId, string jsonData)
    {
        var profile = _profileRegistry.GetProfile(printerId);
        var templatePath = _resolver.Resolve(receiptType, profile.Id);
        return RenderTemplate(templatePath, profile, jsonData);
    }

    public List<PrintCommand> Render(int receiptType, string profileId, string jsonData)
    {
        var profile = _profileRegistry.GetProfile(profileId);
        var templatePath = _resolver.Resolve(receiptType, profileId);
        return RenderTemplate(templatePath, profile, jsonData);
    }

    public List<PrintCommand> RenderTemplate(string templatePath, PrinterProfile profile, string jsonData)
    {
        var templateSource = _storage.Load(templatePath);
        if (templateSource == null)
        {
            throw new FileNotFoundException($"Template not found: {templatePath}");
        }

        return RenderSource(templateSource, profile, jsonData);
    }

    public List<PrintCommand> RenderSource(string templateSource, PrinterProfile profile, string jsonData)
    {
        var template = Parse(templateSource);
        var interpreter = new TemplateInterpreter(profile, _culture);
        return interpreter.Interpret(template, jsonData);
    }

    public TemplateValidationResult Validate(string templateSource, PrinterProfile? profile = null)
    {
        var validator = new TemplateValidator(profile);
        return validator.Validate(templateSource);
    }

    public TemplateValidationResult ValidateTemplate(string templatePath, PrinterProfile? profile = null)
    {
        var templateSource = _storage.Load(templatePath);
        if (templateSource == null)
        {
            return TemplateValidationResult.WithErrors(
                new TemplateError(
                    TemplateErrorCode.RTL001_UnexpectedToken,
                    $"Template not found: {templatePath}",
                    new SourcePosition(0, 0)));
        }

        return Validate(templateSource, profile);
    }

    public void ReloadTemplates()
    {
        _storage.Reload();
    }

    private TemplateNode Parse(string templateSource)
    {
        var lexer = new Lexer(templateSource);
        var lexerResult = lexer.Tokenize();

        if (lexerResult.HasErrors)
        {
            var firstError = lexerResult.Errors.First();
            throw new InterpreterException(
                $"Lexer error: {firstError.Message}",
                firstError.Position);
        }

        var parser = new Parser(lexerResult.Tokens);
        var parseResult = parser.Parse();

        if (parseResult.HasErrors)
        {
            var firstError = parseResult.Errors.First();
            throw new InterpreterException(
                $"Parse error: {firstError.Message}",
                firstError.Position);
        }

        return parseResult.Template;
    }
}
