using System.IO;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

using AIRuntime.Runtime.Capabilities;
using AIRuntime.Runtime.Capabilities.Permissions;
using AIRuntime.Runtime.Filesystem.Models;


namespace AIRuntime.Runtime.Filesystem
{
    /// <summary>
    /// Creates a directory in the current workspace.
    /// </summary>
    public sealed class CreateDirectoryCapability
        : CapabilityBase<CreateDirectoryArguments>
    {
        private static readonly HashSet<string> UnityAssetRootNames =
            new HashSet<string>(
                new[]
                {
                    "Editor",
                    "Runtime",
                    "Samples",
                    "Scenes",
                    "Tests"
                },
                StringComparer.OrdinalIgnoreCase);


        public override string Name =>
            "create_directory";


        public override string Description =>
            "Creates directoryName directly inside the complete parentPath. Set createParents=true when the user asks to create a complete directory path; missing parent directories and a relative nested directoryName chain are then created safely. Set createParents=false when the user asks to add NAME to PARENT; directoryName must then be one exact name and the exact parent must already exist.";


        public override CapabilityPermission Permission =>
            CapabilityPermission.Write;


        protected override async Task<object> ExecuteAsync(
            CreateDirectoryArguments args,
            CapabilityContext context)
        {
            context.CancellationToken
                .ThrowIfCancellationRequested();


            string directoryPath =
                NormalizeDirectoryPath(
                    args.DirectoryName,
                    args.CreateParents);


            string parentPath =
                await ResolveUnityAssetPathAsync(
                    args.ParentPath,
                    context);


            bool parentExists =
                await context.FileSystem.DirectoryExistsAsync(
                    parentPath);


            if (!parentExists)
            {
                if (!args.CreateParents)
                {
                    throw new DirectoryNotFoundException(
                        "Parent directory does not exist: " +
                        parentPath.Replace(
                            '\\',
                            '/'));
                }


                await context.FileSystem.CreateDirectoryAsync(
                    parentPath);
            }


            string targetPath =
                CombinePath(
                    parentPath,
                    RemoveRepeatedParentPrefix(
                        parentPath,
                        directoryPath));


            await context.FileSystem.CreateDirectoryAsync(
                targetPath);


            context.CancellationToken
                .ThrowIfCancellationRequested();


            bool exists =
                await context.FileSystem.DirectoryExistsAsync(
                    targetPath);


            if (!exists)
            {
                throw new IOException(
                    $"Directory creation did not produce the requested path: {targetPath}");
            }


            string normalizedPath =
                targetPath.Replace(
                    '\\',
                    '/');


            return new FileSystemMutationResult
            {
                Succeeded =
                    true,

                Path =
                    normalizedPath,

                Message =
                    "Directory created successfully.",

                ResponseInstruction =
                    "Copy the Path field exactly when reporting the result. Do not replace slashes or periods with underscores."
            };
        }


        private static string NormalizeDirectoryPath(
            string directoryName,
            bool allowNestedPath)
        {
            if (string.IsNullOrWhiteSpace(directoryName) ||
                !string.Equals(
                    directoryName,
                    directoryName.Trim(),
                    StringComparison.Ordinal))
            {
                throw new ValidationException(
                    "DirectoryName must be one directory name without path separators, leading or trailing whitespace, or a trailing period.");
            }

            string normalized =
                directoryName.Replace('\\', '/');

            if (!allowNestedPath || normalized.IndexOf('/') < 0)
            {
                ValidateDirectoryName(directoryName);
                return directoryName;
            }

            if (normalized.StartsWith("/", StringComparison.Ordinal) ||
                normalized.EndsWith("/", StringComparison.Ordinal) ||
                normalized.Contains("//"))
            {
                throw new ValidationException(
                    "DirectoryName must be a relative directory chain without empty, rooted, or trailing segments when createParents=true.");
            }

            string[] segments =
                normalized.Split('/');

            foreach (string segment in segments)
            {
                ValidateDirectoryName(segment);
            }

            return string.Join("/", segments);
        }


        private static string RemoveRepeatedParentPrefix(
            string parentPath,
            string directoryPath)
        {
            string normalizedParent =
                (parentPath ?? string.Empty)
                    .Replace('\\', '/')
                    .TrimEnd('/');

            string repeatedPrefix =
                normalizedParent + "/";

            return directoryPath.StartsWith(
                    repeatedPrefix,
                    StringComparison.OrdinalIgnoreCase)
                ? directoryPath.Substring(repeatedPrefix.Length)
                : directoryPath;
        }


        private static void ValidateDirectoryName(
            string directoryName)
        {
            if (string.IsNullOrWhiteSpace(
                    directoryName) ||
                !string.Equals(
                    directoryName,
                    directoryName.Trim(),
                    StringComparison.Ordinal) ||
                directoryName.EndsWith(
                    ".",
                    StringComparison.Ordinal) ||
                string.Equals(
                    directoryName,
                    ".",
                    StringComparison.Ordinal) ||
                string.Equals(
                    directoryName,
                    "..",
                    StringComparison.Ordinal) ||
                directoryName.IndexOf('/') >= 0 ||
                directoryName.IndexOf('\\') >= 0 ||
                directoryName.IndexOfAny(
                    Path.GetInvalidFileNameChars()) >= 0)
            {
                throw new ValidationException(
                    "DirectoryName must be one directory name without path separators, leading or trailing whitespace, or a trailing period.");
            }
        }


        private static string CombinePath(
            string parentPath,
            string directoryName)
        {
            string normalizedParent =
                parentPath.Replace(
                        '\\',
                        '/')
                    .TrimEnd('/');


            return normalizedParent == "."
                ? directoryName
                : normalizedParent +
                  "/" +
                  directoryName;
        }


        private static async Task<string> ResolveUnityAssetPathAsync(
            string requestedPath,
            CapabilityContext context)
        {
            string normalizedPath =
                requestedPath.Replace(
                    '\\',
                    '/');


            string[] segments =
                normalizedPath.Split(
                    new[]
                    {
                        '/'
                    },
                    StringSplitOptions.RemoveEmptyEntries);


            if (segments.Length == 0 ||
                string.Equals(
                    segments[0],
                    "Assets",
                    StringComparison.OrdinalIgnoreCase) ||
                !UnityAssetRootNames.Contains(
                    segments[0]))
            {
                return requestedPath;
            }


            bool assetsExists =
                await context.FileSystem.DirectoryExistsAsync(
                    "Assets");

            bool assetRootExists =
                assetsExists &&
                await context.FileSystem.DirectoryExistsAsync(
                    "Assets/" +
                    segments[0]);


            return assetRootExists
                ? "Assets/" +
                  normalizedPath
                : requestedPath;
        }
    }
}
