defmodule PhilomenaWeb.StatController do
  use PhilomenaWeb, :controller

  alias Philomena.Elasticsearch
  alias Philomena.Servers.Config
  alias Philomena.Images.Image
  alias Philomena.Comments.Comment
  alias Philomena.Topics.Topic
  alias Philomena.Forums.Forum
  alias Philomena.Posts.Post
  alias Philomena.Users.User
  alias Philomena.Galleries.Gallery
  alias Philomena.Galleries.Interaction
  alias Philomena.Commissions.Commission
  alias Philomena.Commissions.Item
  alias Philomena.Reports.Report
  alias Philomena.Repo
  import Ecto.Query

  def index(conn, _params) do
    {gallery_count, gallery_size, distinct_creators, images_in_galleries} = galleries()
    {open_reports, report_count, response_time} = moderation()
    {open_commissions, commission_items} = commissions()
    {image_aggs, comment_aggs} = aggregations()
    {forums, topics, posts} = forums()
    {users, users_24h} = users()

    render(
      conn,
      "index.html",
      image_aggs: image_aggs,
      comment_aggs: comment_aggs,
      forums_count: forums,
      topics_count: topics,
      posts_count: posts,
      users_count: users,
      users_24h: users_24h,
      open_commissions: open_commissions,
      commission_items: commission_items,
      open_reports: open_reports,
      report_stat_count: report_count,
      response_time: response_time,
      gallery_count: gallery_count,
      gallery_size: gallery_size,
      distinct_creators: distinct_creators,
      images_in_galleries: images_in_galleries,
      title: "Statistics"
    )
  end

  defp aggregations do
    data = Config.get(:aggregation)

    {
      Elasticsearch.search(Image, data["images"]),
      Elasticsearch.search(Comment, data["comments"])
    }
  end

  defp forums do
    forums =
      Forum
      |> where(access_level: "normal")
      |> Repo.aggregate(:count, :id)

    first_topic = Repo.one(first(Topic))
    last_topic = Repo.one(last(Topic))
    first_post = Repo.one(first(Post))
    last_post = Repo.one(last(Post))

    {forums, diff(last_topic, first_topic), diff(last_post, first_post)}
  end

  defp users do
    first_user = Repo.one(first(User))
    last_user = Repo.one(last(User))
    time = DateTime.utc_now() |> DateTime.add(-86400, :second)

    last_24h =
      User
      |> where([u], u.created_at > ^time)
      |> Repo.aggregate(:count, :id)

    {diff(last_user, first_user), last_24h}
  end

  defp galleries do
    gallery_count = Repo.aggregate(Gallery, :count, :id)

    gallery_size =
      Repo.aggregate(Gallery, :avg, :image_count)
      |> Kernel.||(Decimal.new(0))
      |> Decimal.to_float()
      |> trunc()

    distinct_creators =
      Gallery
      |> distinct(:creator_id)
      |> Repo.aggregate(:count, :id)

    first_gi = Repo.one(first(Interaction))
    last_gi = Repo.one(last(Interaction))

    {gallery_count, gallery_size, distinct_creators, diff(last_gi, first_gi)}
  end

  defp commissions do
    open_commissions = Repo.aggregate(where(Commission, open: true), :count, :id)
    commission_items = Repo.aggregate(Item, :count, :id)

    {open_commissions, commission_items}
  end

  defp moderation do
    open_reports = Repo.aggregate(where(Report, open: true), :count, :id)
    first_report = Repo.one(first(Report))
    last_report = Repo.one(last(Report))

    closed_reports =
      Report
      |> where(open: false)
      |> order_by(desc: :created_at)
      |> limit(250)
      |> Repo.all()

    response_time =
      closed_reports
      |> Enum.reduce(0, &(&2 + NaiveDateTime.diff(&1.updated_at, &1.created_at, :second)))
      |> Kernel./(safe_length(closed_reports) * 3600)
      |> trunc()

    {open_reports, diff(last_report, first_report), response_time}
  end

  defp diff(nil, nil), do: 0
  defp diff(%{id: id2}, %{id: id1}), do: id2 - id1

  defp safe_length([]), do: 1
  defp safe_length(list), do: length(list)
end