require 'cgi'
require 'erb'
require 'erubi'
require 'forwardable'
require 'json'
require 'pathname'

require 'debci'
require 'debci/feed'
require 'debci/job'
require 'debci/package'
require 'debci/package_status'
require 'debci/graph'
require 'debci/html_helpers'
require 'fileutils'

module Debci

  class HTML

    class << self

      def update
        html = Debci::HTML.new

        Debci.config.suite_list.each do |suite|
          Debci.config.arch_list.each do |arch|
            json = Debci::HTML::JSON.new(suite, arch)
            json.status
            json.history
            json.packages
          end
        end

        html.index('index.html')
      end

      def update_package(package)
        feed = Debci::HTML::Feed.new
        feed.package(package)
      end
    end

    class Rooted
      attr_reader :root, :datadir

      def initialize
        data_basedir = Debci.config.data_basedir
        @root = Pathname(data_basedir) / datadir
      end
    end

    class JSON < Rooted
      attr_accessor :suite, :arch

      def initialize(suite, arch)
        @datadir = 'status'
        super()
        self.suite = suite
        self.arch = arch
      end

      def status_packages_data
        @status_packages_data ||=
          Debci::PackageStatus.where(suite: suite, arch: arch).includes(:job)
      end

      def status
        data = {
          pass: 0,
          fail: 0,
          neutral: 0,
          tmpfail: 0,
          total: 0,
        }
        status_packages_data.each do |package_status|
          status = package_status.job.status
          data[status.to_sym] += 1
          data[:total] += 1
        end
        data[:date] = Time.now.strftime('%Y-%m-%dT%H:%M:%S')

        output = ::JSON.pretty_generate(data)

        today = root / suite / arch / Time.now.strftime('%Y/%m/%d.json')
        today.parent.mkpath
        today.write(output)

        current = root / suite / arch / 'status.json'
        current.write(output)
      end

      def history
        status_history = (root / suite / arch).glob('[0-9]*/[0-9]*/[0-9]*.json')
        status_history_data = status_history.sort.map { |f| ::JSON.parse(f.read) }

        h = root / suite / arch / 'history.json'
        h.write(::JSON.pretty_generate(status_history_data))
      end

      def packages
        p = root / suite / arch / 'packages.json'
        p.write(::JSON.pretty_generate(status_packages_data.map(&:job).as_json))
      end
    end

    class Feed < Rooted
      def initialize
        @datadir = 'feeds'
        super
      end

      def package(pkg)
        news = pkg.news
        write_feed(news, root / pkg.prefix / "#{pkg.name}.xml") do |feed|
          feed.channel.title = "#{pkg.name} CI news feed"
          feed.channel.about = Debci.config.url_base + "/packages/#{pkg.prefix}/#{pkg.name}/"
          feed.channel.description = [
            "News for #{pkg.name}.",
            'Includes only state transitions (pass-fail, and fail-pass).',
            'Full history is available in the package page and in the published data files.',
          ].join(' ')
        end
      end

      private

      def write_feed(news, feedfile, &block)
        feed = Debci::Feed.new(news, &block)
        feedfile.parent.mkpath
        feed.write(feedfile)
      end
    end

    include Debci::HTMLHelpers
    attr_reader :root_directory

    def initialize(root_directory=Debci.config.html_dir)
      @root_directory = root_directory
      @package_prefixes = Debci::Package.prefixes

      @head = read_config_file('head.html')
      @footer = read_config_file('footer.html')
    end

    def index(filename)
      expand_template(:index, filename)
    end

    private

    def templates
      @templates ||= {}
    end

    def load_template(template)
      binding.eval(read_template(template).src) # rubocop:disable Security/Eval
    end

    def read_template(name)
      templates[name] ||= begin
        filename = File.join(File.dirname(__FILE__), 'html/templates', "#{name}.erb")
        Erubi::Engine.new(
          File.read(filename),
          escape: true,
          filename: filename,
        )
      end
    end

    def expand_template(template, filename)
      directory = File.dirname(filename)

      abs_filename = File.join(root_directory, filename)
      FileUtils.mkdir_p(File.dirname(abs_filename))

      @root = directory.split('/').map { |_| '..' }.join('/')

      html = load_template(:layout) do
        load_template(template)
      end

      File.open(abs_filename, 'w') do |f|
        f.write(html)
      end
    end

    def read_config_file(filename)
      file_path = File.join(Debci.config.config_dir, filename)
      if File.exist?(file_path)
        File.read(file_path)
      end
    end
  end
end