using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Umbraco.Core.IO;
using Umbraco.Tests.BusinessLogic;

namespace Umbraco.Tests.IO
{
    [TestFixture, RequiresSTA]
    internal abstract class AbstractFileSystemTests 
    {
        protected IFileSystem _fileSystem;

        protected AbstractFileSystemTests(IFileSystem fileSystem)
        {
            _fileSystem = fileSystem;
        }

        [Test]
        public void Can_Create_And_Delete_Files()
        {
            _fileSystem.AddFile("test.txt", CreateStream());

            Assert.IsTrue(_fileSystem.FileExists("test.txt"));

            _fileSystem.DeleteFile("test.txt");

           Assert.IsFalse(_fileSystem.FileExists("test.txt"));
        }

        [Test]
        public void Can_Overwrite_File()
        {
            _fileSystem.AddFile("test/test.txt", CreateStream());
            _fileSystem.AddFile("test/test.txt", CreateStream());

            var files = _fileSystem.GetFiles("test");

            Assert.AreEqual(1, files.Count());

            foreach (var file in files)
            {
                _fileSystem.DeleteFile(file);
            }

            _fileSystem.DeleteDirectory("test", true);
        }

        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void Cant_Overwrite_File()
        {
            _fileSystem.AddFile("test.txt", CreateStream());
            _fileSystem.AddFile("test.txt", CreateStream(), false);

            _fileSystem.DeleteFile("test.txt");
        }

        [Test]
        public void Can_Get_Files()
        {
            _fileSystem.AddFile("test/test1.txt", CreateStream());
            _fileSystem.AddFile("test/test2.txt", CreateStream());
            _fileSystem.AddFile("test/test3.txt", CreateStream());
            _fileSystem.AddFile("test/test4.bak", CreateStream());

            var files = _fileSystem.GetFiles("test");

            Assert.AreEqual(4, files.Count());

            files = _fileSystem.GetFiles("test", "*.txt");

            Assert.AreEqual(3, files.Count());

            _fileSystem.DeleteDirectory("test", true);
        }

        [Test]
        public void Can_Read_File()
        {
            _fileSystem.AddFile("test.txt", CreateStream("hello world"));

            var stream = _fileSystem.OpenFile("test.txt");
            var reader = new StreamReader(stream);
            var contents = reader.ReadToEnd();
            reader.Close();

            Assert.AreEqual("hello world", contents);

            _fileSystem.DeleteFile("test.txt");
        }

        [Test]
        public void Can_Get_Directories()
        {
            _fileSystem.AddFile("test/sub1/test.txt", CreateStream());
            _fileSystem.AddFile("test/sub2/test.txt", CreateStream());
            _fileSystem.AddFile("test/sub3/test.txt", CreateStream());

            var dirs = _fileSystem.GetDirectories("test");

            Assert.AreEqual(3, dirs.Count());
            Assert.IsTrue(_fileSystem.DirectoryExists("test/sub1"));
            Assert.IsTrue(_fileSystem.DirectoryExists("test/sub2"));
            Assert.IsTrue(_fileSystem.DirectoryExists("test/sub3"));

            _fileSystem.DeleteDirectory("test", true);
        }

        [Test]
        public void Can_Get_File_Dates()
        {
            _fileSystem.DeleteFile("test.txt");

            _fileSystem.AddFile("test.txt", CreateStream());

            var created = _fileSystem.GetCreated("test.txt");
            var modified = _fileSystem.GetLastModified("test.txt");

            Assert.AreEqual(DateTime.Today.Year, created.Year);
            Assert.AreEqual(DateTime.Today.Month, created.Month);
            Assert.AreEqual(DateTime.Today.Date, created.Date);

            Assert.AreEqual(DateTime.Today.Year, modified.Year);
            Assert.AreEqual(DateTime.Today.Month, modified.Month);
            Assert.AreEqual(DateTime.Today.Date, modified.Date);

            _fileSystem.DeleteFile("test.txt");
        }

        [Test]
        public void Can_Get_File_Url()
        {
            _fileSystem.AddFile("test.txt", CreateStream());

            var url = _fileSystem.GetUrl("test.txt");

            Assert.AreEqual(ConstructUrl("test.txt"), url);

             _fileSystem.DeleteFile("test.txt");
        }

        [Test]
        public void Can_Convert_Full_Path_And_Url_To_Relative_Path()
        {
            _fileSystem.AddFile("test.txt", CreateStream());

            var url = _fileSystem.GetUrl("test.txt");
            var fullPath = _fileSystem.GetFullPath("test.txt");

            Assert.AreNotEqual("test.txt", url);
            Assert.AreNotEqual("test.txt", fullPath);

            Assert.AreEqual("test.txt", _fileSystem.GetRelativePath(url));
            Assert.AreEqual("test.txt", _fileSystem.GetRelativePath(fullPath));

            _fileSystem.DeleteFile("test.txt");
        }

        [Test]
        public void Can_Get_Size()
        {
            var stream = CreateStream();
            var streamLength = stream.Length;
            _fileSystem.AddFile("test.txt", stream);

            Assert.AreEqual(streamLength, _fileSystem.GetSize("test.txt"));

            _fileSystem.DeleteFile("test.txt");
        }

        #region Helper Methods

        protected Stream CreateStream(string contents = null)
        {
            if(string.IsNullOrEmpty(contents))
                contents = "test";

            var bytes = Encoding.UTF8.GetBytes(contents);
            var stream = new MemoryStream(bytes);

            return stream;
        }

        protected abstract string ConstructUrl(string path);

        #endregion
    }
}
