using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StellaOps.Vexer.Core;

namespace StellaOps.Vexer.Export;

public sealed class FileSystemArtifactStoreOptions
{
    public string RootPath { get; set; } = ".";

    public bool OverwriteExisting { get; set; } = false;
}

public sealed class FileSystemArtifactStore : IVexArtifactStore
{
    private readonly IFileSystem _fileSystem;
    private readonly FileSystemArtifactStoreOptions _options;
    private readonly ILogger<FileSystemArtifactStore> _logger;

    public FileSystemArtifactStore(
        IOptions<FileSystemArtifactStoreOptions> options,
        ILogger<FileSystemArtifactStore> logger,
        IFileSystem? fileSystem = null)
    {
        ArgumentNullException.ThrowIfNull(options);
        _options = options.Value;
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _fileSystem = fileSystem ?? new FileSystem();

        if (string.IsNullOrWhiteSpace(_options.RootPath))
        {
            throw new ArgumentException("RootPath must be provided for FileSystemArtifactStore.", nameof(options));
        }

        var root = _fileSystem.Path.GetFullPath(_options.RootPath);
        _fileSystem.Directory.CreateDirectory(root);
        _options.RootPath = root;
    }

    public async ValueTask<VexStoredArtifact> SaveAsync(VexExportArtifact artifact, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(artifact);

        var relativePath = BuildArtifactPath(artifact.ContentAddress, artifact.Format);
        var destination = _fileSystem.Path.Combine(_options.RootPath, relativePath);
        var directory = _fileSystem.Path.GetDirectoryName(destination);
        if (!string.IsNullOrEmpty(directory))
        {
            _fileSystem.Directory.CreateDirectory(directory);
        }

        if (_fileSystem.File.Exists(destination) && !_options.OverwriteExisting)
        {
            _logger.LogInformation("Artifact {Digest} already exists at {Path}; skipping write.", artifact.ContentAddress.ToUri(), destination);
        }
        else
        {
            await using var stream = _fileSystem.File.Create(destination);
            await stream.WriteAsync(artifact.Content, cancellationToken).ConfigureAwait(false);
        }

        var location = destination.Replace(_options.RootPath, string.Empty).TrimStart(_fileSystem.Path.DirectorySeparatorChar, _fileSystem.Path.AltDirectorySeparatorChar);

        return new VexStoredArtifact(
            artifact.ContentAddress,
            location,
            artifact.Content.Length,
            artifact.Metadata);
    }

    public ValueTask DeleteAsync(VexContentAddress contentAddress, CancellationToken cancellationToken)
    {
        var path = MaterializePath(contentAddress);
        if (path is not null && _fileSystem.File.Exists(path))
        {
            _fileSystem.File.Delete(path);
        }

        return ValueTask.CompletedTask;
    }

    public ValueTask<Stream?> OpenReadAsync(VexContentAddress contentAddress, CancellationToken cancellationToken)
    {
        var path = MaterializePath(contentAddress);
        if (path is null || !_fileSystem.File.Exists(path))
        {
            return ValueTask.FromResult<Stream?>(null);
        }

        Stream stream = _fileSystem.File.OpenRead(path);
        return ValueTask.FromResult<Stream?>(stream);
    }

    private static string BuildArtifactPath(VexContentAddress address, VexExportFormat format)
    {
        var formatSegment = format.ToString().ToLowerInvariant();
        var safeDigest = address.Digest.Replace(':', '_');
        var extension = GetExtension(format);
        return Path.Combine(formatSegment, safeDigest + extension);
    }

    private string? MaterializePath(VexContentAddress address)
    {
        ArgumentNullException.ThrowIfNull(address);
        var sanitized = address.Digest.Replace(':', '_');

        foreach (VexExportFormat format in Enum.GetValues(typeof(VexExportFormat)))
        {
            var candidate = _fileSystem.Path.Combine(_options.RootPath, format.ToString().ToLowerInvariant(), sanitized + GetExtension(format));
            if (_fileSystem.File.Exists(candidate))
            {
                return candidate;
            }
        }

        // fallback: direct root search with common extensions
        foreach (var extension in new[] { ".json", ".jsonl" })
        {
            var candidate = _fileSystem.Path.Combine(_options.RootPath, sanitized + extension);
            if (_fileSystem.File.Exists(candidate))
            {
                return candidate;
            }
        }

        return null;
    }

    private static string GetExtension(VexExportFormat format)
        => format switch
        {
            VexExportFormat.Json => ".json",
            VexExportFormat.JsonLines => ".jsonl",
            VexExportFormat.OpenVex => ".json",
            VexExportFormat.Csaf => ".json",
            _ => ".bin",
        };
}

public static class FileSystemArtifactStoreServiceCollectionExtensions
{
    public static IServiceCollection AddVexFileSystemArtifactStore(this IServiceCollection services, Action<FileSystemArtifactStoreOptions>? configure = null)
    {
        if (configure is not null)
        {
            services.Configure(configure);
        }

        services.AddSingleton<IVexArtifactStore, FileSystemArtifactStore>();
        return services;
    }
}
