# frozen_string_literal: true

module IpynbDiff
  class InvalidNotebookError < StandardError
  end

  # Returns a markdown version of the Jupyter Notebook
  class Transformer
    require 'json'
    require 'yaml'
    require 'output_transformer'

    @cell_decorator = :html
    @include_metadata = true


    def initialize(include_metadata: true, cell_decorator: :html)
      @include_metadata = include_metadata
      @cell_decorator = cell_decorator
      @output_transformer = OutputTransformer.new
    end

    def validate_notebook(notebook)
      notebook_json = JSON.parse(notebook)

      return notebook_json if notebook_json.key?('cells')

      raise InvalidNotebookError
    rescue JSON::ParserError
      raise InvalidNotebookError
    end

    def transform(notebook)
      notebook_json = validate_notebook(notebook)
      transformed_blocks = notebook_json['cells'].map do |cell|
        decorate_cell(transform_cell(cell, notebook_json), cell)
      end

      transformed_blocks.prepend(transform_metadata(notebook_json)) if @include_metadata
      transformed_blocks.join("\n")
    end

    def decorate_cell(rows, cell)
      tags = cell['metadata']&.fetch('tags', [])
      type = cell['cell_type'] || 'raw'

      case @cell_decorator
      when :html
        rows.prepend(%(<div class="cell #{type}" data-id="#{cell['id']}" data-tags="#{tags&.join(' ')}">\n\n))
            .append("\n</div>\n")
      when :percent
        rows.prepend(%(%% Cell type:#{type} id:#{cell['id']} tags:#{tags&.join(',')}\n\n))
      else
        rows
      end.join('')
    end

    def transform_cell(cell, notebook)
      cell['cell_type'] == 'code' ? transform_code_cell(cell, notebook) : transform_text_cell(cell)
    end

    def decorate_output(output_rows, output)
      if @cell_decorator == :html
        output_rows.prepend(%(\n<div class="output #{output['output_type']}">\n\n)).append("\n</div>\n")
      else
        output_rows.prepend(%(\n%%%% Output: #{output['output_type']}\n\n))
      end
    end

    def transform_code_cell(cell, notebook)
      [
        %(``` #{notebook.dig('metadata', 'kernelspec', 'language') || ''}\n),
        *cell['source'],
        "\n```\n",
        *cell['outputs'].map { |output| transform_output(output) }
      ]
    end

    def transform_output(output)
      transformed = @output_transformer.transform(output)

      decorate_output(transformed, output).join('') if transformed
    end

    def transform_text_cell(cell)
      source = cell['source']
      (source.is_a?(Array) ? source : [source]).append("\n")
    end

    def transform_metadata(notebook_json)
      {
        'jupyter' => {
          'kernelspec' => notebook_json['metadata']['kernelspec'],
          'language_info' => notebook_json['metadata']['language_info'],
          'nbformat' => notebook_json['nbformat'],
          'nbformat_minor' => notebook_json['nbformat_minor']
        }
      }.to_yaml + "---\n"
    end
  end
end
