using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Rar;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.Compressors.Rar;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Rar;

namespace SharpCompress.Archives.Rar;

public partial class RarArchive
#if NET8_0_OR_GREATER
    : IArchiveOpenable<IRarArchive, IRarAsyncArchive>,
        IMultiArchiveOpenable<IRarArchive, IRarAsyncArchive>
#endif
{
    public static ValueTask<IRarAsyncArchive> OpenAsyncArchive(
        string filePath,
        ReaderOptions? readerOptions = null,
        CancellationToken cancellationToken = default
    )
    {
        cancellationToken.ThrowIfCancellationRequested();
        filePath.NotNullOrEmpty(nameof(filePath));
        return new((IRarAsyncArchive)OpenArchive(new FileInfo(filePath), readerOptions));
    }

    public static IRarArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
    {
        filePath.NotNullOrEmpty(nameof(filePath));
        var fileInfo = new FileInfo(filePath);
        return new RarArchive(
            new SourceStream(
                fileInfo,
                i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo),
                readerOptions ?? ReaderOptions.ForFilePath
            )
        );
    }

    public static IRarArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null)
    {
        fileInfo.NotNull(nameof(fileInfo));
        return new RarArchive(
            new SourceStream(
                fileInfo,
                i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo),
                readerOptions ?? ReaderOptions.ForFilePath
            )
        );
    }

    public static IRarArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
    {
        stream.RequireReadable();
        stream.RequireSeekable();

        return new RarArchive(
            new SourceStream(stream, _ => null, readerOptions ?? ReaderOptions.ForExternalStream)
        );
    }

    public static IRarArchive OpenArchive(
        IReadOnlyList<FileInfo> fileInfos,
        ReaderOptions? readerOptions = null
    )
    {
        fileInfos.NotNull(nameof(fileInfos));
        var files = fileInfos;
        return new RarArchive(
            new SourceStream(
                files[0],
                i => i < files.Count ? files[i] : null,
                readerOptions ?? ReaderOptions.ForFilePath
            )
        );
    }

    public static IRarArchive OpenArchive(
        IReadOnlyList<Stream> streams,
        ReaderOptions? readerOptions = null
    )
    {
        var strms = streams.RequireReadable().RequireSeekable().ToList();
        return new RarArchive(
            new SourceStream(
                strms[0],
                i => i < strms.Count ? strms[i] : null,
                readerOptions ?? ReaderOptions.ForExternalStream
            )
        );
    }

    public static ValueTask<IRarAsyncArchive> OpenAsyncArchive(
        Stream stream,
        ReaderOptions? readerOptions = null,
        CancellationToken cancellationToken = default
    )
    {
        cancellationToken.ThrowIfCancellationRequested();
        return new((IRarAsyncArchive)OpenArchive(stream, readerOptions));
    }

    public static ValueTask<IRarAsyncArchive> OpenAsyncArchive(
        FileInfo fileInfo,
        ReaderOptions? readerOptions = null,
        CancellationToken cancellationToken = default
    )
    {
        cancellationToken.ThrowIfCancellationRequested();
        return new((IRarAsyncArchive)OpenArchive(fileInfo, readerOptions));
    }

    public static ValueTask<IRarAsyncArchive> OpenAsyncArchive(
        IReadOnlyList<Stream> streams,
        ReaderOptions? readerOptions = null,
        CancellationToken cancellationToken = default
    )
    {
        cancellationToken.ThrowIfCancellationRequested();
        return new((IRarAsyncArchive)OpenArchive(streams, readerOptions));
    }

    public static ValueTask<IRarAsyncArchive> OpenAsyncArchive(
        IReadOnlyList<FileInfo> fileInfos,
        ReaderOptions? readerOptions = null,
        CancellationToken cancellationToken = default
    )
    {
        cancellationToken.ThrowIfCancellationRequested();
        return new((IRarAsyncArchive)OpenArchive(fileInfos, readerOptions));
    }

    public static bool IsRarFile(string filePath) => IsRarFile(new FileInfo(filePath));

    public static bool IsRarFile(FileInfo fileInfo)
    {
        if (!fileInfo.Exists)
        {
            return false;
        }
        using Stream stream = fileInfo.OpenRead();
        return IsRarFile(stream);
    }

    public static bool IsRarFile(Stream stream, ReaderOptions? options = null)
    {
        try
        {
            MarkHeader.Read(stream, true, options?.LookForHeader ?? false);
            return true;
        }
        catch
        {
            return false;
        }
    }

    public static async ValueTask<bool> IsRarFileAsync(
        Stream stream,
        ReaderOptions? options = null,
        CancellationToken cancellationToken = default
    )
    {
        cancellationToken.ThrowIfCancellationRequested();
        try
        {
            await MarkHeader
                .ReadAsync(stream, true, options?.LookForHeader ?? false, cancellationToken)
                .ConfigureAwait(false);
            return true;
        }
        catch
        {
            return false;
        }
    }
}
