<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
    <PropertyGroup>
        <RootNamespace>AliasVault.Client</RootNamespace>
        <TargetFramework>net10.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>enable</ImplicitUsings>
        <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
        <BuildVersion>$([System.DateTime]::UtcNow.ToString("yyyy-MM-dd HH:mm:ss"))</BuildVersion>
        <WasmBuildNative>true</WasmBuildNative>
        <LangVersion>14</LangVersion>
        <BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
    </PropertyGroup>

    <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
        <DocumentationFile>bin\Debug\net10.0\AliasVault.Client.xml</DocumentationFile>
        <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
        <CacheBuster>dev</CacheBuster>
        <NoWarn>NU1903;WASM0001</NoWarn>
        <!-- No fingerprinting in debug mode -->
        <BlazorScript>_framework/blazor.webassembly.js</BlazorScript>
    </PropertyGroup>

    <PropertyGroup Condition="'$(Configuration)' == 'Release'">
        <!-- Enable .NET 10 static asset fingerprinting for cache busting (only in Release) -->
        <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
        <BlazorScript>_framework/blazor.webassembly#[.{fingerprint}].js</BlazorScript>
        <DebugSymbols>false</DebugSymbols>
        <DocumentationFile>bin\Release\net10.0\AliasVault.Client.xml</DocumentationFile>
        <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
        <Optimize>True</Optimize>
        <CacheBuster>$([System.DateTime]::UtcNow.ToString("yyyyMMddHHmmss"))</CacheBuster>
        <WasmDebugLevel>0</WasmDebugLevel>
        <!-- Suppress IL2037 linker errors for EF Core reflection-based APIs -->
        <!-- Suppress WASM0001 warnings for varargs in SQLitePCLRaw (sqlite3_config/sqlite3_db_config are not called at runtime) -->
        <NoWarn>NU1903;IL2037;WASM0001</NoWarn>
    </PropertyGroup>

    <UsingTask TaskName="ReplaceText" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
        <ParameterGroup>
            <InputFile ParameterType="System.String" Required="true" />
            <OutputFile ParameterType="System.String" Required="true" />
            <CacheBuster ParameterType="System.String" Required="true" />
            <BuildVersion ParameterType="System.String" Required="true" />
            <BlazorScript ParameterType="System.String" Required="true" />
            <Minify ParameterType="System.Boolean" Required="false" />
        </ParameterGroup>
        <Task>
            <Code Type="Fragment" Language="cs">
                <![CDATA[
        string content = File.ReadAllText(InputFile);
        content = content.Replace("@CacheBuster", CacheBuster).Replace("@BuildVersion", BuildVersion).Replace("@BlazorScript", BlazorScript);

        if (Minify)
        {
            // Extract and preserve script/style blocks
            var scriptBlocks = new System.Collections.Generic.List<string>();
            var styleBlocks = new System.Collections.Generic.List<string>();

            content = System.Text.RegularExpressions.Regex.Replace(content, @"(<script[^>]*>)(.*?)(</script>)",
                m => { scriptBlocks.Add(m.Groups[2].Value); return m.Groups[1].Value + "___SCRIPT_" + (scriptBlocks.Count - 1) + "___" + m.Groups[3].Value; },
                System.Text.RegularExpressions.RegexOptions.Singleline);

            content = System.Text.RegularExpressions.Regex.Replace(content, @"(<style[^>]*>)(.*?)(</style>)",
                m => { styleBlocks.Add(m.Groups[2].Value); return m.Groups[1].Value + "___STYLE_" + (styleBlocks.Count - 1) + "___" + m.Groups[3].Value; },
                System.Text.RegularExpressions.RegexOptions.Singleline);

            // Extract and preserve the header comment (contains ASCII art and license)
            var headerMatch = System.Text.RegularExpressions.Regex.Match(content, @"(<!--\s*╔[\s\S]*?Build:[\s\S]*?-->)");
            string headerComment = headerMatch.Success ? headerMatch.Groups[1].Value : "";
            if (headerMatch.Success)
            {
                content = content.Replace(headerComment, "___HEADER_COMMENT___");
            }

            // Remove all other HTML comments
            content = System.Text.RegularExpressions.Regex.Replace(content, @"<!--.*?-->", "", System.Text.RegularExpressions.RegexOptions.Singleline);

            // Collapse whitespace between tags
            content = System.Text.RegularExpressions.Regex.Replace(content, @">\s+<", "><");

            // Remove leading/trailing whitespace from lines and collapse newlines
            content = System.Text.RegularExpressions.Regex.Replace(content, @"^\s+", "", System.Text.RegularExpressions.RegexOptions.Multiline);
            content = System.Text.RegularExpressions.Regex.Replace(content, @"\s+$", "", System.Text.RegularExpressions.RegexOptions.Multiline);
            content = System.Text.RegularExpressions.Regex.Replace(content, @"\r?\n", "");
            content = System.Text.RegularExpressions.Regex.Replace(content, @"  +", " ");

            // Restore script blocks (minify each one)
            for (int i = 0; i < scriptBlocks.Count; i++)
            {
                var script = scriptBlocks[i];
                // Remove JS single-line comments (but not URLs with //)
                script = System.Text.RegularExpressions.Regex.Replace(script, @"(?<!:)//[^\n]*", "");
                // Remove JS multi-line comments
                script = System.Text.RegularExpressions.Regex.Replace(script, @"/\*.*?\*/", "", System.Text.RegularExpressions.RegexOptions.Singleline);
                // Collapse all whitespace including newlines
                script = System.Text.RegularExpressions.Regex.Replace(script, @"\s+", " ");
                script = script.Trim();
                content = content.Replace("___SCRIPT_" + i + "___", script);
            }

            // Restore style blocks (minify each one)
            for (int i = 0; i < styleBlocks.Count; i++)
            {
                var style = styleBlocks[i];
                // Remove CSS comments
                style = System.Text.RegularExpressions.Regex.Replace(style, @"/\*.*?\*/", "", System.Text.RegularExpressions.RegexOptions.Singleline);
                style = System.Text.RegularExpressions.Regex.Replace(style, @"\s+", " ");
                style = System.Text.RegularExpressions.Regex.Replace(style, @"\s*([{};:,>+~])\s*", "$1");
                style = style.Trim();
                content = content.Replace("___STYLE_" + i + "___", style);
            }

            // Restore header comment at the end (preserving its formatting)
            if (headerMatch.Success)
            {
                content = content.Replace("___HEADER_COMMENT___", "\n" + headerComment + "\n");
            }
        }

        File.WriteAllText(OutputFile, content);
        Log.LogMessage(MessageImportance.High, (Minify ? "Minified and replaced" : "Replaced") + " content in " + OutputFile);
]]>
            </Code>
        </Task>
    </UsingTask>

    <Target Name="NpmInstall" BeforeTargets="NpmBuild" Condition="!Exists('node_modules')">
        <Exec Command="npm install" WorkingDirectory="$(ProjectDir)" />
    </Target>

    <Target Name="NpmBuild" BeforeTargets="GenerateCacheBustedIndexHtml">
        <Exec Command="npm run build:js" WorkingDirectory="$(ProjectDir)" />
    </Target>

    <Target Name="GenerateCacheBustedIndexHtml" BeforeTargets="Build">
        <ReplaceText InputFile="wwwroot/index.template.html" OutputFile="wwwroot/index.html" CacheBuster="$(CacheBuster)" BuildVersion="$(BuildVersion)" BlazorScript="$(BlazorScript)" Minify="true" />
    </Target>

    <ItemGroup>
        <PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
        <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="10.0.9" />
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.9" />
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="10.0.9" />
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.9" PrivateAssets="all" />
        <PackageReference Include="Microsoft.Extensions.Http" Version="10.0.9" />
        <PackageReference Include="Microsoft.Extensions.Localization" Version="10.0.9" />
        <PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="10.0.9" />
        <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
          <PrivateAssets>all</PrivateAssets>
          <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
        <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0">
          <PrivateAssets>all</PrivateAssets>
          <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
        <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.301">
          <PrivateAssets>all</PrivateAssets>
          <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
    </ItemGroup>

    <ItemGroup>
      <Content Update="wwwroot\appsettings.json">
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      </Content>
      <Content Update="wwwroot\appsettings.Development.json" Condition="Exists('wwwroot\appsettings.Development.json')">
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      </Content>
      <Content Include="..\..\.dockerignore">
        <Link>.dockerignore</Link>
      </Content>
      <AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
    </ItemGroup>


    <ItemGroup>
      <ProjectReference Include="..\Databases\AliasClientDb\AliasClientDb.csproj" />
      <ProjectReference Include="..\Shared\AliasVault.RazorComponents\AliasVault.RazorComponents.csproj" />
      <ProjectReference Include="..\Shared\AliasVault.Shared.Core\AliasVault.Shared.Core.csproj" />
      <ProjectReference Include="..\Shared\AliasVault.Shared\AliasVault.Shared.csproj" />
      <ProjectReference Include="..\Utilities\Cryptography\AliasVault.Cryptography.Client\AliasVault.Cryptography.Client.csproj" />
      <ProjectReference Include="..\Utilities\AliasVault.ImportExport\AliasVault.ImportExport.csproj" />
      <ProjectReference Include="..\Utilities\AliasVault.TotpGenerator\AliasVault.TotpGenerator.csproj" />
      <ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
      <ServiceWorker Include="wwwroot\service-worker.published.js">
        <PublishedContent>wwwroot/service-worker.published.js</PublishedContent>
      </ServiceWorker>
    </ItemGroup>
</Project>
