using FaceAiSharp;
using FaceAiSharp.Extensions;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;

namespace CrestronOpenCvSharp.Capture;

public class FacialRecognition
{
    private readonly IFaceDetectorWithLandmarks _detector;
    private readonly IFaceEmbeddingsGenerator _recognizer;
    private readonly string? _baseDirectory;

    private Image<Rgb24>? _image;
    private float[]? _referenceEmbeddings;

    private readonly Dictionary<string, string> _faceImagesDict;
    private string? FaceImagePath { get; set; }
    
    public FacialRecognition(string? baseDirectory)
    {
        _baseDirectory = baseDirectory;
        _detector = FaceAiSharpBundleFactory.CreateFaceDetectorWithLandmarks();
        _recognizer = FaceAiSharpBundleFactory.CreateFaceEmbeddingsGenerator();

        if (_baseDirectory != null) 
            FaceImagePath = Path.Combine(_baseDirectory, "aligned.png");

        // Let's load the default stuff in this dictionary
        _faceImagesDict = new Dictionary<string, string>
        {
            { "Yuri Staal", "https://ise2025.local.staal.one/VirtualControl/MA/Rooms/MYFIRSTAI/Html/yuri.jpg" },
            { "Toine C. Leerentveld", "https://ise2025.local.staal.one/VirtualControl/MA/Rooms/MYFIRSTAI/Html/toine.jpg" },
            { "Oliver Hall", "https://ise2025.local.staal.one/VirtualControl/MA/Rooms/MYFIRSTAI/Html/oliver.jpg" }
        };
    }

    public bool CheckForFace(string imageFilePath)
    {
        
        try
        {
            // Load the photo
            var photo = File.ReadAllBytes(imageFilePath);
            // Convert it
            _image = Image.Load<Rgb24>(photo);
            // Detect faces in this photo
            var faces = _detector.DetectFaces(_image);

            if (faces.Count != 0)
            {
                _recognizer.AlignFaceUsingLandmarks(_image, faces.First().Landmarks!);
                _referenceEmbeddings = _recognizer.GenerateEmbedding(_image);
                _image.Save(FaceImagePath!);
                Console.WriteLine("Aligned faces!");
            }
            else
            {
                Console.WriteLine("No faces were found!");
            }

            // Return true or false
            return faces.Any();
        }
        catch (Exception e)
        {
            Console.WriteLine($"Exception detecting faces: {e.Message}");
            throw;
        }
    }

    public async Task<string?> CompareFaces()
    {
        foreach (var (name, value) in _faceImagesDict)
        {
            var faceImage = await LoadImageAsync(value);
            var detectedFace = _detector.DetectFaces(faceImage).FirstOrDefault();

            // Generate embedding for the detected face
            _recognizer.AlignFaceUsingLandmarks(faceImage, detectedFace.Landmarks!);
            var faceEmbedding = _recognizer.GenerateEmbedding(faceImage);

            // Compare embeddings
            var similarity = _referenceEmbeddings?.Dot(faceEmbedding);
            Console.WriteLine($"Similarity with {name}: {similarity}");
            if (similarity >= 0.42)
            {
                //Console.WriteLine("Assessment: Both pictures show the same person.");
                return name;
            }
        }
        return null;
    }

    public void AddPersonToDatabase(string name)
    {
        var shortName = name.Replace(" ", "");
        // Copy the aligned image to a new image
        if (_baseDirectory != null)
        {
            var newFile = Path.Combine(_baseDirectory, $"{shortName}.jpg");
            Console.WriteLine($"Saved new image to {newFile}");
            File.Copy(FaceImagePath!, newFile, overwrite: true);
        }

        _faceImagesDict.Add(name, $"https://ise2025.local.staal.one/VirtualControl/MA/Rooms/MYFIRSTAI/Html/{shortName}.jpg");
        Console.WriteLine($"Added new image to dictionary");
    }
    
    private async Task<Image<Rgb24>> LoadImageAsync(string path)
    {
        using var hc = new HttpClient();
        var imageBytes = await hc.GetByteArrayAsync(path);
        return Image.Load<Rgb24>(imageBytes);
    }
}