using System.Text;
using skyscraper5.Skyscraper;

namespace skyscraper8.Skyscraper.Scraper.Storage.Tar;

public class TarHeader
{
    public TarHeader(string filename, long size)
    {
        Filename = filename;
        Permissions = new UnixFilePermissions();
        Uid = 1000;
        Gid = 1000;
        Size = size;
        ModificationTime = DateTime.Now;
        OwnerUsername = "sophia";
        OwnerGroupName = "sophia";
    }
    
    private string _filename;
    public string Filename
    {
        get => _filename;
        set
        {
            if (value.Length > 99)
                throw new ArgumentOutOfRangeException(nameof(Filename), "Filename must not exceed 99 characters.");
            if (value.Contains("\0"))
                throw new ArgumentOutOfRangeException(nameof(Filename), "Filename must not contain NULL characters.");
            _filename = value;
        }
    }
    
    public UnixFilePermissions Permissions {get; set;}
    public int Uid { get; set; }
    public int Gid { get; set; }
    public long Size { get; set; }
    public DateTime ModificationTime { get; set; }

    private string _username;

    public string OwnerUsername
    {
        get => _username;
        set
        {
            if (value.Length > 31)
                throw new ArgumentOutOfRangeException("Username", "Username must not exceed 31 characters.");
            _username = value;
        }
    }

    private string _groupname;

    public string OwnerGroupName
    {
        get => _groupname;
        set
        {
            if (value.Length > 31)
                throw new ArgumentOutOfRangeException("GroupName", "Group name must not exceed 31 characters.");
            _groupname = value;
        }
    }

    public long GetSizeOnTape()
    {
        long fileSize = Size;
        if (fileSize % 512 == 0)
            return fileSize;
        
        fileSize /= 512;
        fileSize++;
        fileSize *= 512;
        return fileSize;
    }

    public static TarHeader Deserialize(byte[] buffer)
    {
        if (buffer.Length != 512)
            throw new InvalidDataException("Invalid TAR header length");

        throw new NotImplementedException();
    }
    
    public byte[] Serialize()
    {
        byte[] buffer = new byte[512];
        Encoding.UTF8.GetBytes(Filename,0,Filename.Length,buffer,0);

        string numericFilemode = "0000" + Permissions.ToString();
        Encoding.ASCII.GetBytes(numericFilemode, 0, numericFilemode.Length, buffer, 100);
        
        string numericUid = LongToOctal(Gid, 7);
        Encoding.ASCII.GetBytes(numericUid, 0, numericUid.Length, buffer, 108);
        
        string numericGid = LongToOctal(Gid, 7);
        Encoding.ASCII.GetBytes(numericGid, 0, numericGid.Length, buffer, 116);
        
        string numericSize = LongToOctal(Size, 11);
        Encoding.ASCII.GetBytes(numericSize, 0, numericSize.Length, buffer, 124);

        string numericLastModification = LongToOctal(ModificationTime.ToUnixTime(),11);
        Encoding.ASCII.GetBytes(numericLastModification, 0, numericLastModification.Length, buffer, 136);
        
        buffer[257] = (byte)'u';
        buffer[258] = (byte)'s';
        buffer[259] = (byte)'t';
        buffer[260] = (byte)'a';
        buffer[261] = (byte)'r';
        buffer[262] = 0x20;
        buffer[263] = 0x20;
        buffer[264] = 0;
        
        string username = OwnerUsername;
        Encoding.ASCII.GetBytes(username, 0, username.Length, buffer, 265);

        string groupname = OwnerGroupName;
        Encoding.ASCII.GetBytes(groupname, 0, groupname.Length, buffer, 297);
        
        string checksumBlanco = "        ";
        Encoding.ASCII.GetBytes(checksumBlanco, 0, checksumBlanco.Length, buffer, 148);
        long checkSum = 0;
        for (int i = 0; i < 512; ++i)
        {
            checkSum += buffer[i];
        }
        checksumBlanco = LongToOctal(checkSum, 6);
        Encoding.ASCII.GetBytes(checksumBlanco, 0, checksumBlanco.Length, buffer, 148);

        return buffer;
    }

    private static string LongToOctal(long value, int length)
    {
        string result = string.Empty;

        while (value > 0)
        {
            result = (char)('0' + value % 8) + result;
            value = value / 8;
        }

        return new string('0', length - result.Length) + result;
    }
    
    private static long OctalToLong(string value)
    {
        long result = 0;

        for (int i = 0; i < value.Length; ++i)
        {
            result = result * 8 + (value[i] - '0');
        }

        return result;
    }
}
