get '/admin' do
  require_admin
  @banned_sites = Site.select(:username).filter(is_banned: true).order(:username).all
  @nsfw_sites = Site.select(:username).filter(is_nsfw: true).order(:username).all
  erb :'admin'
end

get '/admin/reports' do
  require_admin
  @page = params[:page] ? params[:page].to_i : 1
  @page = 1 if @page < 1
  @per_page = 51

  @moderation_blur_categories = $config['moderation_blur_categories']
  
  @reports = Report.join(:sites, id: :site_id).where(sites__is_deleted: false, sites__is_banned: false).order(:reports__created_at.desc).select_all(:reports).paginate(@page, @per_page)
  @pagination_dataset = @reports
  
  erb :'admin/reports'
end

post '/admin/reports/:report_id/dismiss' do
  require_admin
  content_type :json
  
  report = Report[params[:report_id]]
  return {success: false, error: 'Report not found'}.to_json if report.nil?

  report.destroy

  # Dismiss any other reports for the site
  DB[:reports].where(site_id: report.site_id).delete

  {success: true, message: 'Report dismissed'}.to_json
end

post '/admin/site_files/train' do
  require_admin
  site = Site[params[:site_id]]
  site_file = site.site_files_dataset.where(path: params[:path]).first
  not_found if site_file.nil?
  site.untrain site_file.path
  site.train site_file.path, params[:classifier]
  'ok'
end

get '/admin/usage' do
  require_admin
  today = Date.today
  current_month = Date.new today.year, today.month, 1
  start_month = Date.new(2016, 2, 1)

  # Build date range for batch query
  months = []
  month = current_month
  until month < start_month
    months << month
    month = month.prev_month
  end

  # Batch query for all monthly stats at once using proper date comparisons
  monthly_stats_query = <<-SQL
    SELECT
      DATE_TRUNC('month', created_at) as month,
      SUM(views) as views,
      SUM(hits) as hits,
      SUM(bandwidth) as bandwidth
    FROM daily_site_stats
    WHERE created_at >= ?
      AND created_at <= ?
    GROUP BY DATE_TRUNC('month', created_at)
    ORDER BY month DESC
  SQL

  monthly_data = DB[monthly_stats_query, start_month, current_month.next_month].all

  # Convert to hash for easy lookup
  stats_by_month = {}
  monthly_data.each do |row|
    month_key = row[:month].to_date
    stats_by_month[month_key] = {
      views: row[:views] || 0,
      hits: row[:hits] || 0,
      bandwidth: row[:bandwidth] || 0
    }
  end

  # Batch query for all popular sites data at once
  popular_sites_query = <<-SQL
    SELECT
      DATE_TRUNC('month', stats.created_at) as month,
      sites.username,
      SUM(stats.bandwidth) as bandwidth
    FROM stats
    LEFT JOIN sites ON sites.id = stats.site_id
    WHERE stats.created_at >= ?
      AND stats.created_at <= ?
      AND sites.username IS NOT NULL
    GROUP BY DATE_TRUNC('month', stats.created_at), sites.username
  SQL

  all_popular_sites = DB[popular_sites_query, start_month, current_month.next_month].all

  # Group popular sites by month and get top 50 for each
  popular_by_month = {}
  all_popular_sites.group_by { |row| row[:month].to_date }.each do |month_key, sites|
    popular_by_month[month_key] = sites
      .sort_by { |s| -(s[:bandwidth] || 0) }
      .take(50)
      .map { |s| { username: s[:username], bandwidth: s[:bandwidth] } }
  end

  # Combine the results
  @monthly_stats = []
  months.each do |month|
    stats = stats_by_month[month]
    next unless stats && stats[:views] != 0 && stats[:hits] != 0 && stats[:bandwidth] != 0

    @monthly_stats.push({
      views: stats[:views],
      hits: stats[:hits],
      bandwidth: stats[:bandwidth],
      date: month,
      popular_sites: popular_by_month[month] || []
    })
  end

  erb :'admin/usage'
end

get '/admin/email' do
  require_admin
  erb :'admin/email'
end

get '/admin/stats' do
  require_admin
  @stats = {
    total_hosted_site_hits: DB['SELECT SUM(hits) FROM sites'].first[:sum],
    total_hosted_site_views: DB['SELECT SUM(views) FROM sites'].first[:sum],
    total_site_changes: DB['select max(changed_count) from sites'].first[:max],
    total_sites: Site.count
  }

  # Start with the date of the first created site
  start = Site.select(:created_at).
               exclude(created_at: nil).
               order(:created_at).
               first[:created_at].to_date

  runner = start
  monthly_stats = []
  now = Date.today

  while Date.new(runner.year, runner.month, 1) <= Date.new(now.year, now.month, 1)
    # Get sites created in this time range
    sites_in_range = Site.where(created_at: runner..runner.next_month)
    sites_from_start = Site.where(created_at: start..runner.next_month)
    
    supporters_count = Site.where(created_at: start..runner.next_month, parent_site_id: nil)
                          .where(
                            Sequel.|(
                              ~{stripe_customer_id: nil} & ~{stripe_subscription_id: nil} & {plan_ended: [false, nil]} & ~{plan_type: ['free', 'special']},
                              {paypal_active: true}
                            )
                          ).count
    
    monthly_stats.push(
      date: runner,
      sites_created: sites_in_range.count,
      total_from_start: sites_from_start.count,
      supporters: supporters_count,
    )

    runner = runner.next_month
  end

  @stats[:monthly_stats] = monthly_stats
  
  @stats[:current_supporters] = Site.where(parent_site_id: nil)
                                    .where(
                                      Sequel.|(
                                        ~{stripe_customer_id: nil} & ~{stripe_subscription_id: nil} & {plan_ended: [false, nil]} & ~{plan_type: ['free', 'special']},
                                        {paypal_active: true}
                                      )
                                    ).count
  @stats[:supporter_percentage] = (@stats[:current_supporters].to_f / @stats[:total_sites] * 100).round(2)

  erb :'admin/stats'
end

get '/admin/ban_history' do
  require_admin

  @title = 'Ban History'
  @page = params[:page] ? params[:page].to_i : 1
  @page = 1 if @page < 1
  @per_page = 51

  @sites = Site.where(is_banned: true).order(:banned_at.desc).exclude(banned_at: nil).  paginate(@page, @per_page)
  @pagination_dataset = @sites

  erb :'admin/ban_history'
end

post '/admin/email' do
  require_admin
  %i{subject body}.each do |k|
    if params[k].nil? || params[k].empty?
      flash[:error] = "#{k.capitalize} is missing."
      redirect '/admin/email'
    end
  end

  sites = Site.newsletter_sites

  day = 0

  until sites.empty?
    seconds = 0.0
    queued_sites = []
    Site::EMAIL_BLAST_MAXIMUM_PER_DAY.times {
      break if sites.empty?
      queued_sites << sites.pop
    }

    queued_sites.each do |site|
      EmailWorker.perform_at((day.days.from_now + seconds), {
        from: Site::FROM_EMAIL,
        to: site.email,
        subject: params[:subject],
        body: params[:body]
      })
      seconds += 0.5
    end

    day += 1
  end

  flash[:success] = "#{sites.length} emails have been queued, #{Site::EMAIL_BLAST_MAXIMUM_PER_DAY} per day."
  redirect '/'
end

post '/admin/ban' do
  require_admin
  if params[:usernames].empty?
    flash[:error] = 'no usernames provided'
    redirect request.referrer
  end

  usernames = params[:usernames].split("\n").collect {|u| u.strip}

  deleted_count = 0
  ip_deleted_count = 0

  usernames.each do |username|
    next if username == ''
    site = Site[username: username]
    next if site.nil? || site.is_banned

    if !params[:classifier].empty?
      site.untrain 'index.html'
      site.train 'index.html', params[:classifier]
    end

    site.ban!
    deleted_count += 1

    if !params[:ban_using_ips].empty? && IPAddress.valid?(site.ip)
      sites = Site.filter(ip: site.ip, is_banned: false).all
      sites.each do |s|
        next if usernames.include?(s.username)
        s.ban!
      end
      ip_deleted_count += 1
    end

    if params[:classifier] == 'spam' || params[:classifier] == 'phishing'
      next unless IPAddress.valid?(site.ip)
      StopForumSpamWorker.perform_async(
        username: site.username,
        email: site.email,
        ip: site.ip,
        classifier: params[:classifier]
      )
    end
  end

  flash[:success] = "#{ip_deleted_count + deleted_count} sites have been banned, including #{ip_deleted_count} matching IPs."
  redirect request.referrer
end

post '/admin/unban' do
  require_admin
  site = Site[username: params[:username]]

  if site.nil?
    flash[:error] = 'User not found'
    redirect request.referrer
  end

  if !site.is_banned
    flash[:error] = 'Site is not banned'
    redirect request.referrer
  end

  site.unban!

  flash[:success] = "Site #{site.username} was unbanned."
  redirect request.referrer
end

post '/admin/mark_nsfw' do
  require_admin
  site = Site[username: params[:username]]

  if site.nil?
    flash[:error] = 'User not found'
    redirect request.referrer
  end

  site.is_nsfw = true
  site.admin_nsfw = true
  site.save_changes validate: false

  flash[:success] = 'MISSION ACCOMPLISHED'
  redirect request.referrer
end

post '/admin/unmark_nsfw' do
  require_admin
  site = Site[username: params[:username]]

  if site.nil?
    flash[:error] = 'User not found'
    redirect request.referrer
  end

  site.is_nsfw = false
  site.admin_nsfw = false
  site.save_changes validate: false

  flash[:success] = 'Site unmarked as NSFW'
  redirect request.referrer
end

post '/admin/mark_moderated' do
  require_admin

  not_found if params[:site_id].blank?
  site = Site[params[:site_id]]
  not_found if site.nil?

  site_ids = Site.moderation_dataset.select(:id).where{created_at <= site.created_at}.all.collect {|s| s.id}

  DB[:sites].where(id: site_ids).update(needs_moderation: false)

  remaining_ds = Site.moderation_dataset.order(:created_at.desc)
  remaining_ds = remaining_ds.paginate(1, Site::BROWSE_PAGINATION_LENGTH)
  last_page = remaining_ds.page_count
  last_page = 1 if last_page < 1

  uri = Addressable::URI.parse request.referer
  query = Rack::Utils.parse_query uri.query
  query['page'] = last_page.to_s
  uri.query = Rack::Utils.build_query(query)
  redirect uri.to_s
end

post '/admin/feature' do
  require_admin
  site = Site[username: params[:username]]

  if site.nil?
    flash[:error] = 'User not found'
    redirect request.referrer
  end

  site.featured_at = Time.now
  site.save_changes(validate: false)
  flash[:success] = 'Site has been featured.'
  redirect request.referrer
end

post '/admin/verify_email' do
  require_admin
  site = Site[username: params[:username]]

  if site.nil?
    flash[:error] = 'User not found'
    redirect request.referrer
  end

  if site.email_confirmed
    flash[:error] = 'Email is already confirmed'
    redirect request.referrer
  end

  site.email_confirmed = true
  site.save_changes(validate: false)
  flash[:success] = "Email for #{site.username} has been manually verified."
  redirect request.referrer
end

post '/admin/site/change_password' do
  require_admin
  site = Site[username: params[:username]]
  not_found if site.nil?

  unless site.parent?
    flash[:error] = 'Password can only be changed on parent sites.'
    redirect "/admin/site/#{site.username}"
  end

  new_password = params[:new_password].to_s
  site.password = new_password
  site.valid?

  if new_password != params[:new_password_confirm].to_s
    site.errors.add :password, 'New passwords do not match.'
  end

  if site.errors.empty?
    site.password_reset_token = nil
    site.password_reset_confirmed = false
    site.save_changes

    site.send_email(
      subject: '[Neocities] Your password has been changed',
      body: Tilt.new('./views/templates/email/password_changed.erb', pretty: true).render(self)
    )

    flash[:success] = 'Password changed.'
  else
    flash[:error] = site.errors.first.last.first
  end

  redirect "/admin/site/#{site.username}"
end

get '/admin/masquerade/:username' do
  require_admin
  site = Site[username: params[:username]]
  not_found if site.nil?
  session[:id] = site.id
  redirect '/'
end

get %r{/admin/site/(.+)} do |username_or_email_or_domain|
  require_admin
  ident = request.path_info.sub(%r{\A/admin/site/}, '')

  if ident.blank?
    flash[:error] = 'username or email or domain required'
    redirect '/admin'
  end

  ident = ident.to_s.strip
  ident = ident.sub(%r{\A(https?):/+}, '\1://')

  begin
    parsed_ident = Addressable::URI.parse(ident)
    ident = parsed_ident.host if parsed_ident.host
  rescue Addressable::URI::InvalidURIError
  end

  ident = ident.split('/').first.to_s.downcase

  if ident =~ /@/
    @site = Site[email: ident]
  elsif ident.end_with?('.neocities.org')
    @site = Site[username: ident.sub(/\.neocities\.org\z/, '')]
  elsif ident =~ /.+\..+$/
    @site = Site.where(domain: ident).first
  else
    @site = Site[username: ident]
  end

  if @site.nil?
    flash[:error] = "site not found"
    redirect request.referrer
  end

  @title = "Site Info - #{@site.username}"
  @moderation_blur_categories = Array($config['moderation_blur_categories'])
  @moderation_blur_paths = Report
    .where(site_id: @site.id, type: @moderation_blur_categories)
    .all
    .map do |report|
      path = report.site_file_path.to_s.sub(%r{\A/+}, '')
      path.empty? ? 'index.html' : path
    end
    .uniq

  erb :'admin/site'
end
