# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import setup_modules  # pylint: disable=unused-import

from chromium_src.tools.metrics.structured.codegen_util import FileInfo, Util
from chromium_src.tools.metrics.structured.code_generator import (
  EventTemplateBase,
  ProjectInfoBase,
  EventInfoBase,
  MetricInfoBase,
)


class ProjectInfoTs(ProjectInfoBase):
  """Codegen-related info about a project in Typescript."""

  def __init__(self, project):
    super().__init__(project, 'webui')


class EventInfoTs(EventInfoBase):
  """Codegen-related info about an event in Typescript."""

  def __init__(self, event, project_info):
    super().__init__(event, project_info)

    if self.is_event_sequence == 'true':
      self.systemUptime = (
        '{microseconds: BigInt(Math.floor(Date.now() * 1000))}'
      )
    else:
      self.systemUptime = 'null'


class MetricInfoTs(MetricInfoBase):
  """Codegen-related info about a metric in Typescript."""

  def __init__(self, metric, project_info, event_info):
    super().__init__(metric)

    self.project_name = project_info.name
    self.event_name = event_info.name

    if metric.type == 'hmac-string':
      self.ts_type = 'number'
      self.type_enum = 'hmacValue'
    elif metric.type == 'int':
      self.ts_type = 'bigint'
      self.type_enum = 'longValue'
    elif metric.type == 'raw-string':
      self.ts_type = 'string'
      self.type_enum = 'rawStrValue'
    elif metric.type == 'double':
      self.ts_type = 'number'
      self.type_enum = 'doubleValue'
    elif metric.type == 'int-array':
      # todo(b/341807121): support int array in Typescript.
      self.type_enum = ''
    else:
      if self.is_enum:
        self.ts_type = project_info.name + '_' + metric.type
        self.type_enum = 'intValue'
      else:
        raise ValueError('Invalid metric type.')


class TemplateTypescript(EventTemplateBase):
  """Template for producing Typescript code from structured.xml, to
  be used in WebUI pages."""

  def __init__(
    self,
    model,
    dirname,
    basename,
    file_template,
    project_template,
    enum_template,
    event_template,
    metric_template,
    metric_field_template,
    metric_build_code_template,
  ):
    super().__init__(
      model,
      dirname,
      basename,
      file_template,
      project_template,
      enum_template,
      event_template,
      metric_template,
    )
    self.metric_field_template = metric_field_template
    self.metric_build_code_template = metric_build_code_template

  def write_file(self):
    file_info = FileInfo(self.dirname, self.basename)
    with open(file_info.filepath, 'w') as f:
      f.write(self._stamp_file())

  def _stamp_file(self):
    project_code = ''.join(
      self._stamp_project(project) for project in self.model.projects
    )
    return self.file_template.format(project_code=project_code)

  def _stamp_project(self, project):
    project_info = ProjectInfoTs(project)
    if project_info.should_codegen:
      event_code = ''.join(
        self._stamp_event(project_info, event) for event in project.events
      )
      enum_code = '\n\n'.join(
        self._stamp_enum(project_info, enum) for enum in project.enums
      )
      return self.project_template.format(
        project=project, enum_code=enum_code, event_code=event_code
      )
    return ''

  def _stamp_event(self, project_info, event):
    event_info = EventInfoTs(event, project_info)
    metric_code = ''.join(
      self._stamp_metric(project_info, event_info, metric)
      for metric in event.metrics
    )
    metric_fields = ''.join(
      self._stamp_metric_fields(project_info, event_info, metric)
      for metric in event.metrics
    )
    metric_build_code = ''.join(
      self._stamp_metric_build_code(project_info, event_info, metric)
      for metric in event.metrics
    )
    return self.event_template.format(
      event=event_info,
      metric_fields=metric_fields,
      metric_code=metric_code,
      metric_build_code=metric_build_code,
    )

  def _stamp_metric(self, project_info, event_info, metric):
    metric_info = MetricInfoTs(metric, project_info, event_info)
    return self.metric_template.format(metric=metric_info)

  def _stamp_metric_fields(self, project_info, event_info, metric):
    metric_info = MetricInfoTs(metric, project_info, event_info)
    return self.metric_field_template.format(metric=metric_info)

  def _stamp_metric_build_code(self, project_info, event_info, metric):
    metric_info = MetricInfoTs(metric, project_info, event_info)
    return self.metric_build_code_template.format(metric=metric_info)

  def _stamp_enum(self, project_info, enum):
    variants = ',\n'.join(
      ['{v.name} = {v.value}'.format(v=v) for v in enum.variants]
    )
    return self.enum_template.format(
      project_info=project_info, enum=enum, variants=variants
    )
