using System.Text;
using Inspectron.Fastbuffer.Interfaces;
using OpenCvSharp;
using Serilog;

namespace Inspectron.Fastbuffer;

public class IndexFile
{
    private readonly string _indexFilePath;
    private readonly int _bufferSize;
    private readonly IFilesystem _filesystem;
    private readonly string? _bufferPath;

    private const int RecordSize = 512;
    private const int IndexSize = 4;
    private const int HeaderSize = 4;

    public IndexFile(string path, int bufferSize, IFilesystem filesystem)
    {
        _indexFilePath = path;
        _bufferSize = bufferSize;
        _filesystem = filesystem;
        ValidateFile();
        _bufferPath = Path.Combine(Path.GetDirectoryName(_indexFilePath), "..");
    }

    private void ValidateFile()
    {
        if (!File.Exists(_indexFilePath))
        {
            CreateFile();
        }
        // check file size
        var fileInfo = new FileInfo(_indexFilePath);
        if (fileInfo.Length != _bufferSize * RecordSize + HeaderSize)
        {
            UpdateFile();
        }
    }

    private void UpdateFile()
    {
        using var fileStream = File.Open(_indexFilePath, FileMode.Open);
        var actualSize = fileStream.Length;
        if (actualSize < _bufferSize+ HeaderSize)
        {
            fileStream.Seek(0, SeekOrigin.End);
            var buffer = new byte[_bufferSize * RecordSize - actualSize];
            fileStream.Write(buffer, HeaderSize, buffer.Length);
        }
    }

    private void CreateFile()
    {
        using var fileStream = File.Create(_indexFilePath);
        var buffer = new byte[_bufferSize* RecordSize + HeaderSize];
        fileStream.Write(buffer, 0, buffer.Length);
        fileStream.Seek(0, SeekOrigin.Begin);
        var bytes = BitConverter.GetBytes(-1);
        fileStream.Write(bytes, 0, bytes.Length);
    }

    public int GetCurrentArtifactId()
    {
        using var fileStream = File.Open(_indexFilePath, FileMode.Open);
        using var reader = new BinaryReader(fileStream);
        return reader.ReadInt32();
    }

    public void SetCurrentArtifactId(int id)
    {
        using var fileStream = File.Open(_indexFilePath, FileMode.Open);
        using var writer = new BinaryWriter(fileStream);
        writer.Write(id);
    }

    public string GetArtifactPath(int id)
    {
        using var fileStream = File.Open(_indexFilePath, FileMode.Open);
        fileStream.Seek(id * RecordSize+HeaderSize, SeekOrigin.Begin);
        byte[] bytes = new byte[RecordSize];
        fileStream.Read(bytes, 0, RecordSize);
        return Encoding.UTF8.GetString(bytes).TrimEnd('\0');
    }

    public int GetNextId()
    {
        var currentId = GetCurrentArtifactId();
        if (currentId + 1 >= _bufferSize)
        {
            currentId = 0;
        }
        else
        {
            currentId++;
        }
        return currentId;
    }

    public void DeleteArtifact(int id)
    {
        var relPath = GetArtifactPath(id);
        var filePath = Path.Combine(_bufferPath, relPath);
        try
        {
            _filesystem.Delete(filePath);
        }
        catch (Exception e)
        {
            Log.Error("Error deleting artifact {id}: {message}",id,e.Message);
        }
        
    }
    
    public void WriteNextArtifact(Mat data, string fileName)
    {
        var id = GetNextId();
        Log.Debug("Writing artifact {id}",id);

        var existingPath = GetArtifactPath(id);
        Log.Debug("Existing path: {existingPath}", existingPath);

        DeleteArtifact(id);
        var filePath = Path.Combine(_bufferPath, fileName);
        _filesystem.Write(filePath, data);
        SetCurrentArtifactId(id);
        using var fileStream = File.Open(_indexFilePath, FileMode.Open);
        fileStream.Seek(id * RecordSize+ HeaderSize, SeekOrigin.Begin);
        var bytes = Encoding.UTF8.GetBytes(fileName);
        fileStream.Write(bytes, 0, bytes.Length);
        var padding = new byte[RecordSize - bytes.Length];
        fileStream.Write(padding, 0, padding.Length);
    }


}