using System;
using System.IO;
using UnityEngine;

namespace Davanci.Nexus
{
    // Cloud + local storage seams for NexusCloudSaveCore.
    public interface INexusCloudProvider
    {
        bool Available { get; }
        void Push(byte[] data, Action<bool> onDone);
        void Pull(Action<bool, byte[]> onDone);
    }

    // Where the local mirror lives. Swappable so tests never touch disk.
    public interface INexusLocalStore
    {
        byte[] Read();
        void Write(byte[] data);
        void Clear();
    }

    // Editor / unsupported-platform fallback: no cloud, local mirror still works.
    public sealed class LocalOnlyProvider : INexusCloudProvider
    {
        public bool Available => false;
        public void Push(byte[] data, Action<bool> onDone) => onDone?.Invoke(false);
        public void Pull(Action<bool, byte[]> onDone) => onDone?.Invoke(false, null);
    }

    // Test double: a single shared in-memory cloud slot with synchronous callbacks.
    // Two cores sharing one instance simulate two devices syncing through the same cloud.
    public sealed class InMemoryCloudProvider : INexusCloudProvider
    {
        byte[] _stored;
        public bool Available { get; set; } = true;
        public int PushCount { get; private set; }
        public int PullCount { get; private set; }

        public void Push(byte[] data, Action<bool> onDone)
        {
            if (!Available) { onDone?.Invoke(false); return; }
            _stored = data == null ? null : (byte[])data.Clone();
            PushCount++;
            onDone?.Invoke(true);
        }

        public void Pull(Action<bool, byte[]> onDone)
        {
            if (!Available) { onDone?.Invoke(false, null); return; }
            PullCount++;
            onDone?.Invoke(true, _stored == null ? null : (byte[])_stored.Clone());
        }

        // Test helpers: seed the slot as if another device wrote it, or inspect it.
        public void SeedRaw(byte[] data) => _stored = data == null ? null : (byte[])data.Clone();
        public byte[] PeekRaw() => _stored == null ? null : (byte[])_stored.Clone();
    }

    // Default local mirror: a single file under persistentDataPath.
    public sealed class FileLocalStore : INexusLocalStore
    {
        readonly string _path;

        public FileLocalStore(string fileName = "nexus_save.dat")
            => _path = Path.Combine(Application.persistentDataPath, fileName);

        public byte[] Read()
        {
            try { return File.Exists(_path) ? File.ReadAllBytes(_path) : null; }
            catch (Exception e) { Debug.LogWarning($"[Nexus] Local save read failed: {e.Message}"); return null; }
        }

        public void Write(byte[] data)
        {
            try { File.WriteAllBytes(_path, data ?? Array.Empty<byte>()); }
            catch (Exception e) { Debug.LogWarning($"[Nexus] Local save write failed: {e.Message}"); }
        }

        public void Clear()
        {
            try { if (File.Exists(_path)) File.Delete(_path); }
            catch (Exception e) { Debug.LogWarning($"[Nexus] Local save clear failed: {e.Message}"); }
        }
    }

    // In-memory local mirror for tests.
    public sealed class InMemoryLocalStore : INexusLocalStore
    {
        byte[] _data;
        public InMemoryLocalStore(byte[] seed = null) => _data = seed;
        public byte[] Read() => _data == null ? null : (byte[])_data.Clone();
        public void Write(byte[] data) => _data = data == null ? null : (byte[])data.Clone();
        public void Clear() => _data = null;
    }
}
