#!/usr/bin/env ruby
# frozen_string_literal: true

require "optparse"
require "uri"
require "yaml"

require_relative "../lib/gitlab_quality/test_tooling"

require_relative '../lib/gitlab_quality/test_tooling/code_coverage/category_owners'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/click_house/category_owners_table'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/click_house/coverage_metrics_table'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/click_house/test_file_mappings_table'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/coverage_data'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/lcov_file'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/artifacts'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/test_report'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/test_map'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/test_file_mapping_data'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/source_file_classifier'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/responsibility_classifier'
require_relative '../lib/gitlab_quality/test_tooling/code_coverage/responsibility_patterns_config'

params = {}
required_params = [:test_reports, :coverage_report, :test_map, :clickhouse_url, :clickhouse_database, :clickhouse_username, :clickhouse_shared_database, :responsibility_patterns]

options = OptionParser.new do |opts|
  opts.banner = "Usage: #{$PROGRAM_NAME} [options]"

  opts.separator ""
  opts.separator "Options:"

  opts.on('--test-reports GLOB',
    'Glob pattern for test JSON reports (RSpec or Jest) (e.g., "reports/**/*.json")') do |pattern|
    params[:test_reports] = pattern
  end

  opts.on('--coverage-report PATH', 'Path to the LCOV coverage report (e.g., "coverage/lcov/gitlab.lcov")') do |path|
    params[:coverage_report] = path
  end

  opts.on('--test-map PATH', 'Path to the test map file (e.g., "crystalball/packed-mapping.json.gz")') do |path|
    params[:test_map] = path
  end

  opts.on('--clickhouse-url URL', 'ClickHouse server URL') do |url|
    params[:clickhouse_url] = url
  end

  opts.on('--clickhouse-database DATABASE', 'ClickHouse database name') do |database|
    params[:clickhouse_database] = database
  end

  opts.on('--clickhouse-username USERNAME', 'ClickHouse username') do |username|
    params[:clickhouse_username] = username
  end

  opts.on('--clickhouse-shared-database DATABASE', 'ClickHouse shared database name') do |database|
    params[:clickhouse_shared_database] = database
  end

  opts.on('--responsibility-patterns PATH', 'Path to YAML file with responsibility classification patterns') do |path|
    params[:responsibility_patterns] = path
  end

  opts.separator ""
  opts.separator "Environment variables:"
  opts.separator "  GLCI_CLICKHOUSE_METRICS_PASSWORD   ClickHouse password (required, not passed via CLI for security)"
  opts.separator ""

  opts.on('-h', '--help', 'Show the usage') do
    puts opts
    puts "\nExamples:"
    puts "  #{$PROGRAM_NAME}"
    exit
  end

  opts.on_tail('-v', '--version', 'Show the version') do
    require_relative "../lib/gitlab_quality/test_tooling/version"
    puts "#{$PROGRAM_NAME} : #{GitlabQuality::TestTooling::VERSION}"
    exit
  end

  opts.parse(ARGV)
end

if params.any? && (required_params - params.keys).none?
  clickhouse_password = ENV.fetch('GLCI_CLICKHOUSE_METRICS_PASSWORD', nil)
  if clickhouse_password.to_s.strip.empty?
    puts "Error: GLCI_CLICKHOUSE_METRICS_PASSWORD environment variable must be set and not empty"
    exit 1
  end

  [:clickhouse_url, :clickhouse_database, :clickhouse_username, :clickhouse_shared_database].each do |param|
    if params[param].to_s.strip.empty?
      puts "Error: --#{param.to_s.tr('_', '-')} cannot be empty"
      exit 1
    end
  end

  begin
    uri = URI.parse(params[:clickhouse_url])
    unless uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
      puts "Error: --clickhouse-url must be a valid HTTP or HTTPS URL"
      exit 1
    end
  rescue URI::InvalidURIError
    puts "Error: --clickhouse-url is not a valid URL format"
    exit 1
  end

  artifacts = GitlabQuality::TestTooling::CodeCoverage::Artifacts.new(
    coverage_report: params[:coverage_report],
    test_map: params[:test_map],
    test_reports: params[:test_reports]
  )

  coverage_report = artifacts.coverage_report
  test_map = artifacts.test_map

  code_coverage_by_source_file = GitlabQuality::TestTooling::CodeCoverage::LcovFile.new(coverage_report).parsed_content

  test_map_parser = GitlabQuality::TestTooling::CodeCoverage::TestMap.new(test_map)
  source_file_to_tests = test_map_parser.source_to_tests
  test_to_sources = test_map_parser.test_to_sources

  # Process test reports
  tests_to_categories = artifacts.test_reports.reduce({}) do |combined_hash, test_report_file|
    file_categories = GitlabQuality::TestTooling::CodeCoverage::TestReport.new(test_report_file).tests_to_categories
    combined_hash.merge(file_categories) { |_, old_val, new_val| (old_val + new_val).uniq }
  end

  category_owners = GitlabQuality::TestTooling::CodeCoverage::CategoryOwners.new

  # Classify source files by type (frontend, backend, etc.)
  source_file_classifier = GitlabQuality::TestTooling::CodeCoverage::SourceFileClassifier.new
  source_file_types = source_file_classifier.classify(code_coverage_by_source_file.keys)

  # Load responsibility patterns from config file
  begin
    patterns_config = GitlabQuality::TestTooling::CodeCoverage::ResponsibilityPatternsConfig.new(
      params[:responsibility_patterns]
    )
  rescue GitlabQuality::TestTooling::CodeCoverage::ResponsibilityPatternsConfig::ConfigError => e
    puts "Error: #{e.message}"
    exit 1
  end

  # Classify test files as responsible or dependent
  responsibility_classifier = GitlabQuality::TestTooling::CodeCoverage::ResponsibilityClassifier.new(
    test_to_sources,
    responsible_patterns: patterns_config.responsible_patterns,
    dependent_patterns: patterns_config.dependent_patterns
  )
  test_classifications = responsibility_classifier.classify_tests

  coverage_data = GitlabQuality::TestTooling::CodeCoverage::CoverageData.new(
    code_coverage_by_source_file,
    source_file_to_tests,
    tests_to_categories,
    category_owners.feature_categories_to_teams,
    source_file_types,
    test_classifications
  )

  clickhouse_data = {
    url: params[:clickhouse_url],
    database: params[:clickhouse_database],
    username: params[:clickhouse_username],
    password: clickhouse_password
  }

  shared_clickhouse_data = {
    url: params[:clickhouse_url],
    database: params[:clickhouse_shared_database],
    username: params[:clickhouse_username],
    password: clickhouse_password
  }

  category_owners_table  = GitlabQuality::TestTooling::CodeCoverage::ClickHouse::CategoryOwnersTable.new(**shared_clickhouse_data)
  coverage_metrics_table = GitlabQuality::TestTooling::CodeCoverage::ClickHouse::CoverageMetricsTable.new(
    category_owners_table: category_owners_table,
    **clickhouse_data
  )
  coverage_metrics_table.push(coverage_data.as_db_table)

  # Export test-to-file mappings
  test_file_mapping_data = GitlabQuality::TestTooling::CodeCoverage::TestFileMappingData.new(
    test_to_sources,
    tests_to_categories: tests_to_categories,
    feature_categories_to_teams: category_owners.feature_categories_to_teams
  )
  test_file_mappings_table = GitlabQuality::TestTooling::CodeCoverage::ClickHouse::TestFileMappingsTable.new(**shared_clickhouse_data)
  test_file_mappings_table.push(test_file_mapping_data.as_db_table)
else
  puts "Missing argument(s). Required arguments are: #{required_params}\nPassed arguments are: #{params}\n"
  puts options
  exit 1
end
