using System;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Providers;

namespace SharpCompress.Writers.SevenZip;

/// <summary>
/// Options for configuring 7z writer behavior.
/// </summary>
public sealed record SevenZipWriterOptions : IWriterOptions
{
    private CompressionType _compressionType;
    private int _compressionLevel;

    /// <summary>
    /// The compression type to use. Supported: LZMA and LZMA2 (default).
    /// </summary>
    public CompressionType CompressionType
    {
        get => _compressionType;
        set
        {
            if (value != CompressionType.LZMA && value != CompressionType.LZMA2)
            {
                throw new ArgumentException(
                    $"SevenZipWriter only supports CompressionType.LZMA and CompressionType.LZMA2. Got: {value}",
                    nameof(value)
                );
            }
            _compressionType = value;
        }
    }

    /// <summary>
    /// Compression level (not used for LZMA in this implementation; reserved for future use).
    /// </summary>
    public int CompressionLevel
    {
        get => _compressionLevel;
        set => _compressionLevel = value;
    }

    /// <summary>
    /// SharpCompress will keep the supplied streams open. Default is true.
    /// </summary>
    public bool LeaveStreamOpen { get; set; } = true;

    /// <summary>
    /// Encoding to use for archive entry names.
    /// </summary>
    public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();

    /// <summary>
    /// An optional progress reporter for tracking compression operations.
    /// </summary>
    public IProgress<ProgressReport>? Progress { get; set; }

    /// <summary>
    /// Buffer size for writer stream copy operations.
    /// </summary>
    public int BufferSize { get; set; } = Constants.BufferSize;

    /// <summary>
    /// Registry of compression providers.
    /// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom implementations.
    /// </summary>
    public CompressionProviderRegistry Providers { get; set; } =
        CompressionProviderRegistry.Default;

    /// <summary>
    /// When true, opts in to a format's optional parallel encode. No writer currently implements
    /// parallel encoding; reserved for future use. Default is false.
    /// </summary>
    public bool EnableParallelism { get; set; }

    /// <summary>
    /// Whether to compress the archive header itself using LZMA.
    /// Default is true, matching standard 7-Zip behavior.
    /// </summary>
    public bool CompressHeader { get; set; } = true;

    /// <summary>
    /// Custom LZMA encoder properties. Null uses defaults (1MB dictionary, 32 fast bytes).
    /// </summary>
    public LzmaEncoderProperties? LzmaProperties { get; set; }

    /// <summary>
    /// Creates a new SevenZipWriterOptions instance with LZMA2 compression (default).
    /// </summary>
    public SevenZipWriterOptions()
    {
        CompressionType = CompressionType.LZMA2;
    }

    /// <summary>
    /// Creates a new SevenZipWriterOptions instance with the specified compression type.
    /// </summary>
    /// <param name="compressionType">The compression type for the archive.</param>
    public SevenZipWriterOptions(CompressionType compressionType)
    {
        CompressionType = compressionType;
    }

    /// <summary>
    /// Creates a new SevenZipWriterOptions instance from an existing WriterOptions instance.
    /// </summary>
    /// <param name="options">The WriterOptions to copy values from.</param>
    public SevenZipWriterOptions(WriterOptions options)
    {
        CompressionType = options.CompressionType;
        CompressionLevel = options.CompressionLevel;
        LeaveStreamOpen = options.LeaveStreamOpen;
        ArchiveEncoding = options.ArchiveEncoding;
        Progress = options.Progress;
        BufferSize = options.BufferSize;
        Providers = options.Providers;
    }

    /// <summary>
    /// Creates a new SevenZipWriterOptions from an existing IWriterOptions instance.
    /// </summary>
    /// <param name="options">The IWriterOptions to copy values from.</param>
    public SevenZipWriterOptions(IWriterOptions options)
    {
        CompressionType = options.CompressionType;
        CompressionLevel = options.CompressionLevel;
        LeaveStreamOpen = options.LeaveStreamOpen;
        ArchiveEncoding = options.ArchiveEncoding;
        Progress = options.Progress;
        BufferSize = options.BufferSize;
        Providers = options.Providers;
    }

    /// <summary>
    /// Implicit conversion from CompressionType to SevenZipWriterOptions.
    /// </summary>
    public static implicit operator SevenZipWriterOptions(CompressionType compressionType) =>
        new(compressionType);
}
