// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
fn escape_html(value : String) -> String {
  value
  .replace_all(old="&", new="&")
  .replace_all(old="<", new="<")
  .replace_all(old=">", new=">")
  .replace_all(old="\"", new=""")
  .replace_all(old="'", new="'")
}

///|
fn preview_element_insert_offset(
  source : String,
  span : @xml.SourceSpan,
) -> Int {
  let mut offset = span.end.offset - 1
  while offset > span.start.offset && source[offset] != '>' {
    offset -= 1
  }
  if offset > span.start.offset && source[offset - 1] == '/' {
    offset - 1
  } else {
    offset
  }
}

///|
fn preview_source(source : String, width : Int, height : Int) -> String {
  let reader = @xml.NamespaceReader::from_string(source)
  let events = reader.read_events_until_eof() catch { _ => return source }
  for event in events {
    match event.kind {
      Start(element) | Empty(element) => {
        if element.name.local_name != "svg" {
          return source
        }
        for attribute in element.attributes {
          if attribute.name.local_name == "viewBox" &&
            attribute.name.namespace_uri is None {
            return source
          }
        }
        // CSS sizing alone does not scale the user space of a root SVG without
        // viewBox. Adapt only the sandbox copy to the Comparison Viewport.
        let offset = preview_element_insert_offset(source, event.span)
        return "\{source.view(end_offset=offset)} viewBox=\"0 0 \{width} \{height}\"\{source.view(start_offset=offset)}"
      }
      _ => ()
    }
  }
  source
}

///|
fn sandbox_document(source : String, width : Int, height : Int) -> String {
  escape_html(
    "\{preview_source(source, width, height)}",
  )
}

///|
/// Render a self-contained interactive HTML presentation of a report.
///
/// The original SVG sources are embedded as sandboxed preview documents. The
/// supplied Structured Report remains the sole source of classification,
/// ordering, magnitude, localization, and Diagnostics; this function does not
/// run comparison again. Review marks and navigation are session-only UI state.
pub fn render_html_report(
  before_svg : String,
  after_svg : String,
  report : StructuredReport,
) -> String {
  let width = report.profile.viewport_width
  let height = report.profile.viewport_height
  let report_json = escape_html(report.to_json_string())
  let before_document = sandbox_document(before_svg, width, height)
  let after_document = sandbox_document(after_svg, width, height)
  let styles = html_report_styles()
  let script = html_report_script()
  let html =
    $|
    $|
    $|
    $|
    $|SVG Diff Report
    $|
$|

SVG previews

No localization selected

100%
$|

Before

$|

Difference

Rendering difference…
$|

After

$|
Observed contributionConservative candidate
$| $|
html }