// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests;

public class ComponentDeclarationRazorIntegrationTest : RazorIntegrationTestBase
{
    public ComponentDeclarationRazorIntegrationTest()
    {
        // Include this assembly to use types defined in tests.
        BaseCompilation = DefaultBaseCompilation.AddReferences(MetadataReference.CreateFromFile(GetType().Assembly.Location));
    }

    internal override CSharpCompilation BaseCompilation { get; }

    internal override string FileKind => FileKinds.Component;

    internal override bool DeclarationOnly => true;

    [Fact]
    public void DeclarationConfiguration_IncludesFunctions()
    {
        // Arrange & Act
        var component = CompileToComponent(@"
@functions {
    public string Value { get; set; }
}");

        // Assert
        var property = component.GetType().GetProperty("Value");
        Assert.NotNull(property);
        Assert.Same(typeof(string), property.PropertyType);
    }

    [Fact]
    public void DeclarationConfiguration_IncludesInject()
    {
        // Arrange & Act
        var component = CompileToComponent(@"
@inject string Value
");

        // Assert
        var property = component.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance);
        Assert.NotNull(property);
        Assert.Same(typeof(string), property.PropertyType);
    }

    [Fact]
    public void DeclarationConfiguration_IncludesUsings()
    {
        // Arrange & Act
        var component = CompileToComponent(@"
@using System.Text
@inject StringBuilder Value
");

        // Assert
        var property = component.GetType().GetProperty("Value", BindingFlags.NonPublic | BindingFlags.Instance);
        Assert.NotNull(property);
        Assert.Same(typeof(StringBuilder), property.PropertyType);
    }

    [Fact]
    public void DeclarationConfiguration_IncludesInherits()
    {
        // Arrange & Act
        var component = CompileToComponent($@"
@inherits {FullTypeName<BaseClass>()}
");

        // Assert
        Assert.Same(typeof(BaseClass), component.GetType().BaseType);
    }

    [Fact]
    public void DeclarationConfiguration_IncludesImplements()
    {
        // Arrange & Act
        var component = CompileToComponent($@"
@implements {FullTypeName<IDoCoolThings>()}
");

        // Assert
        Assert.Contains(typeof(IDoCoolThings), component.GetType().GetInterfaces());
    }

    [Fact, WorkItem("https://github.com/dotnet/blazor/issues/453")]
    public void DeclarationConfiguration_FunctionsBlockHasLineMappings_MappingsApplyToError()
    {
        // Arrange & Act 1
        var generated = CompileToCSharp(@"
@functions {
    public StringBuilder Builder { get; set; }
}
");

        // Assert 1
        AssertSourceEquals(@"
// <auto-generated/>
#pragma warning disable 1591
#pragma warning disable 0414
#pragma warning disable 0649
#pragma warning disable 0169

namespace Test
{
    #line hidden
    using global::System;
    using global::System.Collections.Generic;
    using global::System.Linq;
    using global::System.Threading.Tasks;
    using global::Microsoft.AspNetCore.Components;
    public partial class TestComponent : global::Microsoft.AspNetCore.Components.ComponentBase
    {
        #pragma warning disable 1998
        protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder)
        {
        }
        #pragma warning restore 1998
#nullable restore
#line 1 ""x:\dir\subdir\Test\TestComponent.cshtml""
            
    public StringBuilder Builder { get; set; }

#line default
#line hidden
#nullable disable
    }
}
#pragma warning restore 1591
", generated);

        // Act 2
        var assembly = CompileToAssembly(generated, throwOnFailure: false);

        // Assert 2
        var diagnostic = Assert.Single(assembly.Diagnostics);

        // This error should map to line 2 of the generated file, the test
        // says 1 because Roslyn's line/column data structures are 0-based.
        var position = diagnostic.Location.GetMappedLineSpan();
        Assert.EndsWith(".cshtml", position.Path);
        Assert.Equal(1, position.StartLinePosition.Line);
    }

    public class BaseClass : IComponent
    {
        public void Attach(RenderHandle renderHandle)
        {
        }

        protected virtual void BuildRenderTree(RenderTreeBuilder builder)
        {
        }

        public Task SetParametersAsync(ParameterView parameters)
        {
            throw new System.NotImplementedException();
        }
    }

    public interface IDoCoolThings
    {
    }
}
