using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;

namespace TestConsole
{
    class Block
    {
        public int Index { get; set; }
        public string PreviousHash { get; set; }
        public string Hash { get; set; }
        public string Data { get; set; }
        public long Timestamp { get; set; }
        public int Nonce { get; set; }

        public Block(int index, string previousHash, string data)
        {
            Index = index;
            PreviousHash = previousHash;
            Data = data;
            Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
            Nonce = 0;
            Hash = CalculateHash();
        }

        public string CalculateHash()
        {
            using (SHA256 sha256 = SHA256.Create())
            {
                string hash = "";
                byte[] inputBytes = Encoding.UTF8.GetBytes($"{Index}{PreviousHash}{Timestamp}{Data}{Nonce}");
                byte[] outputBytes = sha256.ComputeHash(inputBytes);
                hash = Convert.ToBase64String(outputBytes);

                while (hash.Substring(0,3) != "000")
                {
                    Nonce++;
                    inputBytes = Encoding.UTF8.GetBytes($"{Index}{PreviousHash}{Timestamp}{Data}{Nonce}");
                    outputBytes = sha256.ComputeHash(inputBytes);
                    hash = Convert.ToBase64String(outputBytes);
                }
                return hash;
            }
        }
    }

    class Blockchain
    {
        public List<Block> Chain { get; set; }

        public Blockchain()
        {
            Chain = new List<Block>();
            Chain.Add(new Block(0, "", ""));
        }

        public Block GetLatestBlock()
        {
            return Chain[Chain.Count - 1];
        }

        public void AddBlock(string data)
        {
            Block latestBlock = GetLatestBlock();
            int index = latestBlock.Index + 1;
            string previousHash = latestBlock.Hash;
            Chain.Add(new Block(index, previousHash, data));
        }

        public bool IsValid()
        {

            for (int i = 1; i < Chain.Count; i++)
            {
                Block currentBlock = Chain[i];
                Block previusBlock = Chain[i - 1];

                if (currentBlock.PreviousHash != previusBlock.Hash) return false;
                if (currentBlock.Hash != currentBlock.CalculateHash()) return false;
                if (currentBlock.Index != previusBlock.Index + 1) return false;
                return true;
            }

            return false;
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            Blockchain blockchain = new Blockchain();

            blockchain.AddBlock("This is block 1");
            blockchain.AddBlock("This is block 2");
            blockchain.AddBlock("This is block 3");

            var options = new JsonSerializerOptions { WriteIndented = true };
            string blockString = JsonSerializer.Serialize(blockchain.Chain, options);

            Console.WriteLine(blockString);
            /*
            foreach (Block block in blockchain.Chain)
            {
                Console.WriteLine($"Index: {block.Index}");
                Console.WriteLine($"Previous Hash: {block.PreviousHash}");
                Console.WriteLine($"Hash: {block.Hash}");
                Console.WriteLine($"Data: {block.Data}");
                Console.WriteLine($"Timestamp: {block.Timestamp}");
                Console.WriteLine();
            }

            Console.WriteLine(blockchain.IsValid() ? "Block Chen is valid" : "BlockChain is invalid");
            */
        }
    }
}