using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace io.github.hatayama.uLoopMCP
{
    /// <summary>
    /// Lightweight C# source scanner that determines top-level structure without Roslyn.
    /// Tracks string literals, comments, and brace depth to classify source into one of three modes.
    /// </summary>
    internal static class SourceShaper
    {
        public static SourceShapeResult Analyze(string source)
        {
            Debug.Assert(source != null, "source must not be null");

            SourceShapeResult result = new SourceShapeResult();
            int length = source.Length;
            int pos = 0;
            int braceDepth = 0;

            while (pos < length)
            {
                pos = SkipWhitespace(source, pos);
                if (pos >= length) break;

                if (braceDepth == 0)
                {
                    if (TryMatchLineComment(source, pos, out int afterComment))
                    {
                        pos = afterComment;
                        continue;
                    }

                    if (TryMatchBlockComment(source, pos, out int afterBlock))
                    {
                        pos = afterBlock;
                        continue;
                    }

                    if (StartsWithKeyword(source, pos, "using"))
                    {
                        int segmentStart = pos;
                        int afterUsing = pos + 5;
                        afterUsing = SkipWhitespace(source, afterUsing);

                        if (StartsWithKeyword(source, afterUsing, "static"))
                        {
                            int end = FindSemicolon(source, segmentStart);
                            result.UsingDirectives.Add(source.Substring(segmentStart, end - segmentStart + 1).TrimEnd());
                            pos = end + 1;
                            continue;
                        }

                        // "using var" and "using (" are using-statements, not using-directives
                        if (afterUsing < length && (StartsWithKeyword(source, afterUsing, "var") || source[afterUsing] == '('))
                        {
                            result.HasTopLevelStatements = true;
                            int end = FindStatementEnd(source, segmentStart, ref braceDepth);
                            result.TopLevelBodyBuilder.AppendLine(source.Substring(segmentStart, end - segmentStart + 1).TrimEnd());
                            pos = end + 1;
                            continue;
                        }

                        int semiEnd = FindSemicolon(source, segmentStart);
                        result.UsingDirectives.Add(source.Substring(segmentStart, semiEnd - segmentStart + 1).TrimEnd());
                        pos = semiEnd + 1;
                        continue;
                    }

                    if (StartsWithKeyword(source, pos, "namespace"))
                    {
                        result.HasNamespaceDeclaration = true;
                        pos = SkipBlock(source, pos, ref braceDepth);
                        continue;
                    }

                    if (IsTypeDeclarationKeyword(source, pos))
                    {
                        result.HasTypeDeclaration = true;
                        pos = SkipBlock(source, pos, ref braceDepth);
                        continue;
                    }

                    if (StartsWithKeyword(source, pos, "global") && StartsWithKeyword(source, SkipWhitespace(source, pos + 6), "using"))
                    {
                        int segmentStart = pos;
                        int semiEnd = FindSemicolon(source, segmentStart);
                        result.UsingDirectives.Add(source.Substring(segmentStart, semiEnd - segmentStart + 1).TrimEnd());
                        pos = semiEnd + 1;
                        continue;
                    }

                    if (pos < length && source[pos] == '[')
                    {
                        int afterAttr = SkipAttributeBlock(source, pos);
                        int nextNonWs = SkipWhitespace(source, afterAttr);
                        if (nextNonWs < length && IsTypeDeclarationKeyword(source, nextNonWs))
                        {
                            result.HasTypeDeclaration = true;
                            pos = SkipBlock(source, nextNonWs, ref braceDepth);
                            continue;
                        }
                    }

                    if (IsAccessModifier(source, pos))
                    {
                        int afterMod = SkipAccessModifiers(source, pos);
                        if (IsTypeDeclarationKeyword(source, afterMod))
                        {
                            result.HasTypeDeclaration = true;
                            pos = SkipBlock(source, afterMod, ref braceDepth);
                            continue;
                        }
                    }

                    result.HasTopLevelStatements = true;
                    int stmtStart = pos;
                    int stmtEnd = FindStatementEnd(source, stmtStart, ref braceDepth);
                    result.TopLevelBodyBuilder.AppendLine(source.Substring(stmtStart, stmtEnd - stmtStart + 1).TrimEnd());
                    pos = stmtEnd + 1;
                }
                else
                {
                    pos = AdvanceInsideBlock(source, pos, ref braceDepth);
                }
            }

            return result;
        }

        public static string WrapIfNeeded(string source, string namespaceName, string className)
        {
            SourceShapeResult shape = Analyze(source);

            // Raw mode: namespace or type declaration without top-level statements → pass through
            if ((shape.HasNamespaceDeclaration || shape.HasTypeDeclaration) && !shape.HasTopLevelStatements)
            {
                return source;
            }

            // Mixed mode: both type declarations and top-level statements → error
            if ((shape.HasNamespaceDeclaration || shape.HasTypeDeclaration) && shape.HasTopLevelStatements)
            {
                return null;
            }

            // Script mode: wrap top-level statements
            string body = shape.TopLevelBodyBuilder.ToString().TrimEnd();

            bool hasReturn = TopLevelReturnDetector.HasTopLevelReturn(body);
            if (!hasReturn)
            {
                body = string.IsNullOrWhiteSpace(body)
                    ? "return null;"
                    : body + "\nreturn null;";
            }

            return WrapperTemplate.Build(shape.UsingDirectives, namespaceName, className, body);
        }

        internal static int SkipWhitespace(string s, int pos)
        {
            Debug.Assert(s != null, "s must not be null");
            Debug.Assert(pos >= 0, "pos must be non-negative");
            while (pos < s.Length && char.IsWhiteSpace(s[pos])) pos++;
            return pos;
        }

        internal static bool StartsWithKeyword(string s, int pos, string keyword)
        {
            Debug.Assert(s != null, "s must not be null");
            Debug.Assert(keyword != null, "keyword must not be null");
            Debug.Assert(pos >= 0, "pos must be non-negative");
            if (pos + keyword.Length > s.Length) return false;
            for (int i = 0; i < keyword.Length; i++)
            {
                if (s[pos + i] != keyword[i]) return false;
            }
            // Keyword must be followed by non-identifier char
            int afterPos = pos + keyword.Length;
            if (afterPos < s.Length && (char.IsLetterOrDigit(s[afterPos]) || s[afterPos] == '_'))
            {
                return false;
            }
            return true;
        }

        private static bool IsTypeDeclarationKeyword(string s, int pos)
        {
            return StartsWithKeyword(s, pos, "class") ||
                   StartsWithKeyword(s, pos, "struct") ||
                   StartsWithKeyword(s, pos, "interface") ||
                   StartsWithKeyword(s, pos, "enum") ||
                   StartsWithKeyword(s, pos, "record");
        }

        private static bool IsAccessModifier(string s, int pos)
        {
            return StartsWithKeyword(s, pos, "public") ||
                   StartsWithKeyword(s, pos, "internal") ||
                   StartsWithKeyword(s, pos, "private") ||
                   StartsWithKeyword(s, pos, "protected") ||
                   StartsWithKeyword(s, pos, "static") ||
                   StartsWithKeyword(s, pos, "sealed") ||
                   StartsWithKeyword(s, pos, "abstract") ||
                   StartsWithKeyword(s, pos, "partial");
        }

        private static int SkipAccessModifiers(string s, int pos)
        {
            while (pos < s.Length && IsAccessModifier(s, pos))
            {
                int wordEnd = pos;
                while (wordEnd < s.Length && (char.IsLetterOrDigit(s[wordEnd]) || s[wordEnd] == '_')) wordEnd++;
                pos = SkipWhitespace(s, wordEnd);
            }
            return pos;
        }

        private static bool TryMatchLineComment(string s, int pos, out int afterComment)
        {
            afterComment = pos;
            if (pos + 1 < s.Length && s[pos] == '/' && s[pos + 1] == '/')
            {
                int end = pos + 2;
                while (end < s.Length && s[end] != '\n') end++;
                if (end < s.Length) end++; // skip \n
                afterComment = end;
                return true;
            }
            return false;
        }

        private static bool TryMatchBlockComment(string s, int pos, out int afterBlock)
        {
            afterBlock = pos;
            if (pos + 1 < s.Length && s[pos] == '/' && s[pos + 1] == '*')
            {
                int end = pos + 2;
                while (end + 1 < s.Length && !(s[end] == '*' && s[end + 1] == '/')) end++;
                afterBlock = end + 2 < s.Length ? end + 2 : s.Length;
                return true;
            }
            return false;
        }

        private static int FindSemicolon(string s, int pos)
        {
            while (pos < s.Length)
            {
                if (s[pos] == ';') return pos;
                pos = AdvanceOneToken(s, pos);
            }
            return s.Length - 1;
        }

        private static int FindStatementEnd(string s, int pos, ref int braceDepth)
        {
            while (pos < s.Length)
            {
                char c = s[pos];
                if (c == '{')
                {
                    braceDepth++;
                    pos++;
                    while (pos < s.Length && braceDepth > 0)
                    {
                        pos = AdvanceInsideBlock(s, pos, ref braceDepth);
                    }
                    return pos - 1;
                }
                if (c == ';') return pos;
                pos = AdvanceOneToken(s, pos);
            }
            return s.Length - 1;
        }

        private static int SkipBlock(string s, int pos, ref int braceDepth)
        {
            // Advance past keywords until we hit the opening brace
            while (pos < s.Length && s[pos] != '{')
            {
                if (s[pos] == ';') return pos + 1; // forward declaration
                pos = AdvanceOneToken(s, pos);
            }
            if (pos < s.Length && s[pos] == '{')
            {
                braceDepth++;
                pos++;
                while (pos < s.Length && braceDepth > 0)
                {
                    pos = AdvanceInsideBlock(s, pos, ref braceDepth);
                }
            }
            return pos;
        }

        private static int SkipAttributeBlock(string s, int pos)
        {
            Debug.Assert(s[pos] == '[', "SkipAttributeBlock must start at '['");
            int depth = 1;
            pos++;
            while (pos < s.Length && depth > 0)
            {
                if (s[pos] == '[') depth++;
                else if (s[pos] == ']') depth--;
                else pos = AdvanceOneToken(s, pos) - 1; // -1 because loop will pos++ via fallthrough
                pos++;
            }
            return pos;
        }

        private static int AdvanceInsideBlock(string s, int pos, ref int braceDepth)
        {
            char c = s[pos];
            if (c == '{') { braceDepth++; return pos + 1; }
            if (c == '}') { braceDepth--; return pos + 1; }
            return AdvanceOneToken(s, pos);
        }

        internal static int AdvanceOneTokenPublic(string s, int pos)
        {
            return AdvanceOneToken(s, pos);
        }

        private static int AdvanceOneToken(string s, int pos)
        {
            if (pos >= s.Length) return s.Length;
            char c = s[pos];

            // Line comment
            if (c == '/' && pos + 1 < s.Length && s[pos + 1] == '/')
            {
                int end = pos + 2;
                while (end < s.Length && s[end] != '\n') end++;
                return end < s.Length ? end + 1 : s.Length;
            }

            // Block comment
            if (c == '/' && pos + 1 < s.Length && s[pos + 1] == '*')
            {
                int end = pos + 2;
                while (end + 1 < s.Length && !(s[end] == '*' && s[end + 1] == '/')) end++;
                return end + 2 < s.Length ? end + 2 : s.Length;
            }

            // Verbatim string (@"...")
            if (c == '@' && pos + 1 < s.Length && s[pos + 1] == '"')
            {
                int end = pos + 2;
                while (end < s.Length)
                {
                    if (s[end] == '"')
                    {
                        if (end + 1 < s.Length && s[end + 1] == '"') { end += 2; continue; }
                        return end + 1;
                    }
                    end++;
                }
                return s.Length;
            }

            // Raw string literal (""" ... """)
            if (c == '"' && pos + 2 < s.Length && s[pos + 1] == '"' && s[pos + 2] == '"')
            {
                int end = pos + 3;
                while (end + 2 < s.Length)
                {
                    if (s[end] == '"' && s[end + 1] == '"' && s[end + 2] == '"') return end + 3;
                    end++;
                }
                return s.Length;
            }

            // Regular string literal ("...")
            if (c == '"')
            {
                int end = pos + 1;
                while (end < s.Length && s[end] != '"')
                {
                    if (s[end] == '\\') end++; // skip escaped char
                    end++;
                }
                return end < s.Length ? end + 1 : s.Length;
            }

            // Char literal ('x')
            if (c == '\'')
            {
                int end = pos + 1;
                while (end < s.Length && s[end] != '\'')
                {
                    if (s[end] == '\\') end++;
                    end++;
                }
                return end < s.Length ? end + 1 : s.Length;
            }

            // Interpolated string ($"...")
            if (c == '$' && pos + 1 < s.Length && s[pos + 1] == '"')
            {
                return SkipInterpolatedString(s, pos + 2);
            }

            return pos + 1;
        }

        private static int SkipInterpolatedString(string s, int pos)
        {
            int end = pos;
            while (end < s.Length)
            {
                if (s[end] == '\\')
                {
                    end += 2;
                    continue;
                }

                if (s[end] == '{')
                {
                    if (end + 1 < s.Length && s[end + 1] == '{')
                    {
                        end += 2;
                        continue;
                    }

                    end = SkipInterpolationHole(s, end + 1);
                    continue;
                }

                if (s[end] == '"' && end + 1 < s.Length && s[end + 1] == '"')
                {
                    end += 2;
                    continue;
                }

                if (s[end] == '"')
                {
                    return end + 1;
                }

                end++;
            }

            return s.Length;
        }

        private static int SkipInterpolationHole(string s, int pos)
        {
            int depth = 1;
            int end = pos;

            while (end < s.Length && depth > 0)
            {
                if (s[end] == '\\')
                {
                    end += 2;
                    continue;
                }

                if (s[end] == '@' && end + 1 < s.Length && s[end + 1] == '"')
                {
                    end = SkipVerbatimString(s, end + 2);
                    continue;
                }

                if (s[end] == '$' && end + 1 < s.Length && s[end + 1] == '"')
                {
                    end = SkipInterpolatedString(s, end + 2);
                    continue;
                }

                if (s[end] == '"' && end + 2 < s.Length && s[end + 1] == '"' && s[end + 2] == '"')
                {
                    end = SkipRawString(s, end + 3);
                    continue;
                }

                if (s[end] == '"')
                {
                    end = SkipRegularString(s, end + 1);
                    continue;
                }

                if (s[end] == '\'')
                {
                    end = SkipCharLiteral(s, end + 1);
                    continue;
                }

                if (s[end] == '{')
                {
                    depth++;
                    end++;
                    continue;
                }

                if (s[end] == '}')
                {
                    depth--;
                    end++;
                    continue;
                }

                end++;
            }

            return end;
        }

        private static int SkipRegularString(string s, int pos)
        {
            int end = pos;
            while (end < s.Length && s[end] != '"')
            {
                if (s[end] == '\\')
                {
                    end++;
                }

                end++;
            }

            return end < s.Length ? end + 1 : s.Length;
        }

        private static int SkipVerbatimString(string s, int pos)
        {
            int end = pos;
            while (end < s.Length)
            {
                if (s[end] == '"')
                {
                    if (end + 1 < s.Length && s[end + 1] == '"')
                    {
                        end += 2;
                        continue;
                    }

                    return end + 1;
                }

                end++;
            }

            return s.Length;
        }

        private static int SkipRawString(string s, int pos)
        {
            int end = pos;
            while (end + 2 < s.Length)
            {
                if (s[end] == '"' && s[end + 1] == '"' && s[end + 2] == '"')
                {
                    return end + 3;
                }

                end++;
            }

            return s.Length;
        }

        private static int SkipCharLiteral(string s, int pos)
        {
            int end = pos;
            while (end < s.Length && s[end] != '\'')
            {
                if (s[end] == '\\')
                {
                    end++;
                }

                end++;
            }

            return end < s.Length ? end + 1 : s.Length;
        }
    }

    internal sealed class SourceShapeResult
    {
        public List<string> UsingDirectives { get; } = new List<string>();
        public bool HasNamespaceDeclaration { get; set; }
        public bool HasTypeDeclaration { get; set; }
        public bool HasTopLevelStatements { get; set; }
        public StringBuilder TopLevelBodyBuilder { get; } = new StringBuilder();
    }
}
