using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading;

namespace Crysome.Common.Network.FileTransfer;

public sealed class FtReceiver
{
	private const int SackIntervalMs = 200;

	private readonly CrysomeClient _client;

	private uint _id;

	private string _fileName;

	private int _totalChunks;

	private long _totalBytes;

	private byte[] _expectedSha256;

	private byte[][] _chunks;

	private bool[] _received;

	private int _receivedCount;

	private int _highestContiguous = -1;

	private int _lastSackTick;

	private bool _finalised;

	public Action<string, byte[]> OnComplete;

	public Action<string> OnError;

	public FtReceiver(CrysomeClient client)
	{
		_client = client;
	}

	public void HandleStart(FtStartPacket p)
	{
		_id = p.TransferId;
		_fileName = p.FileName;
		_totalChunks = Math.Max(1, p.TotalChunks);
		_totalBytes = p.TotalBytes;
		_expectedSha256 = p.Sha256;
		_chunks = new byte[_totalChunks][];
		_received = new bool[_totalChunks];
		_receivedCount = 0;
		_highestContiguous = -1;
		_lastSackTick = Environment.TickCount - 400;
		_finalised = false;
	}

	public void HandleChunk(FtChunkPacket p)
	{
		if (_chunks == null || p.TransferId != _id || _finalised)
		{
			return;
		}
		int chunkIdx = p.ChunkIdx;
		if (chunkIdx >= 0 && chunkIdx < _totalChunks && !_received[chunkIdx] && p.Data != null && Crc32.Compute(p.Data, 0, p.Data.Length) == p.Crc32)
		{
			_chunks[chunkIdx] = p.Data;
			_received[chunkIdx] = true;
			Interlocked.Increment(ref _receivedCount);
			while (_highestContiguous + 1 < _totalChunks && _received[_highestContiguous + 1])
			{
				_highestContiguous++;
			}
			int tickCount = Environment.TickCount;
			bool num = _receivedCount == _totalChunks;
			if (num || tickCount - _lastSackTick >= 200)
			{
				_lastSackTick = tickCount;
				SendSack();
			}
			if (num)
			{
				Finalise();
			}
		}
	}

	public void HandleAbort(FtAbortPacket p)
	{
		if (p.TransferId == _id)
		{
			OnError?.Invoke("Transfer aborted by sender: " + p.Reason);
		}
	}

	private void SendSack()
	{
		List<int> list = new List<int>();
		int num = _highestContiguous + 1;
		int num2 = Math.Min(_totalChunks, num + 64);
		for (int i = num; i < num2; i++)
		{
			if (!_received[i])
			{
				list.Add(i);
			}
		}
		_client.SendPacket(new FtSackPacket
		{
			TransferId = _id,
			HighestContiguous = _highestContiguous,
			NackList = list.ToArray()
		});
	}

	private void Finalise()
	{
		if (_finalised)
		{
			return;
		}
		_finalised = true;
		byte[] array = new byte[_totalBytes];
		long num = 0L;
		for (int i = 0; i < _totalChunks; i++)
		{
			byte[] array2 = _chunks[i];
			Buffer.BlockCopy(array2, 0, array, (int)num, array2.Length);
			num += array2.Length;
		}
		byte[] array3;
		using (SHA256 sHA = SHA256.Create())
		{
			array3 = sHA.ComputeHash(array);
		}
		bool flag = true;
		for (int j = 0; j < 32; j++)
		{
			if (array3[j] != _expectedSha256[j])
			{
				flag = false;
				break;
			}
		}
		_client.SendPacket(new FtDonePacket
		{
			TransferId = _id,
			Success = flag,
			Message = (flag ? "OK" : "HASH_MISMATCH")
		});
		if (flag)
		{
			OnComplete?.Invoke(_fileName, array);
		}
		else
		{
			OnError?.Invoke("SHA-256 mismatch — file rejected");
		}
	}
}
