defmodule ApiWeb.AttachmentController do
  use ApiWeb, :controller
  require Logger
  import Ecto.Query
  alias Api.Repo

  def index(conn, params) do
    decode = """
    SELECT convert_from(
      decrypt(decode('#{params["idstudyattachment"]}', 'hex')::bytea, '1nf0rm3'::bytea, 'aes'),
      'UTF8'
    );
    """

    idstudyattachment =
      Repo.query!(decode).rows
      |> hd()
      |> hd()
      |> String.to_integer()

    [attachment | _] = get_attachment(idstudyattachment)
    Logger.info("Adjunto: #{inspect(attachment)}")

    case attachment do
      # Caso 1: archivo en filesystem
      %{path: path} when not is_nil(path) ->
        if File.exists?(path) do
          conn
          |> put_resp_content_type("application/#{attachment[:format]}")
          |> put_resp_header("content-disposition", "inline; filename=#{attachment[:fullname]}")
          |> send_file(200, path)
        else
          conn |> send_resp(404, "File not found")
        end

      # Caso 2: archivo en S3 (zip → redirige)
      %{s3_path: s3_path, format: "zip"} when not is_nil(s3_path) ->
        conn |> redirect(external: s3_path)

      # Caso 3: archivo en S3 (otros formatos → preview o descarga)
      %{s3_path: s3_path} when not is_nil(s3_path) ->
        redirect_or_preview(conn, s3_path, attachment[:fullname])

      # Caso 4: ni path ni s3_path → usar campo attachment (bytea)
      %{path: nil, s3_path: nil, attachment: bin} when not is_nil(bin) ->
        content_type = content_type_for(attachment[:format])
        disposition =
          if content_type == "application/octet-stream" do
            "attachment"
          else
            "inline"
          end

        conn
        |> put_resp_content_type(content_type)
        |> put_resp_header(
          "content-disposition",
          "#{disposition}; filename=#{attachment[:fullname]}"
        )
        |> send_resp(200, bin)

      # Caso default
      _ ->
        conn |> send_resp(400, "Invalid attachment")
    end
  end

  def get_attachment(idstudyattachment) do
    query =
      from sa in "studyattachments",
        where: sa.idstudyattachment == ^idstudyattachment,
        select: %{
          attachment: sa.attachment,
          path: sa.path,
          s3_path: sa.s3_path,
          format: sa.format,
          fullname: fragment("concat(name, '.', format)")
        }

    Repo.all(query)
  end

  defp redirect_or_preview(conn, path, fullname) do
    case HTTPoison.get(path, [], [follow_redirect: false, hackney: [pool: :default]]) do
      {:ok, %HTTPoison.Response{status_code: 302, headers: headers}} ->
        handle_redirect(conn, headers, fullname)

      {:ok, %HTTPoison.Response{status_code: 200, body: body, headers: file_headers}} ->
        preview_attachment(conn, body, file_headers, fullname)

      {:error, reason} ->
        Logger.error("Error al obtener el adjunto: #{inspect(reason)}")
        conn |> send_resp(404, "File not found")
    end
  end

  defp handle_redirect(conn, headers, fullname) do
    case List.keyfind(headers, "location", 0) do
      {"location", redirected_url} ->
        Logger.info("Redirigiendo a: #{redirected_url}")

        case HTTPoison.get(redirected_url, [], [follow_redirect: true]) do
          {:ok, %HTTPoison.Response{status_code: 200, body: body, headers: file_headers}} ->
            preview_attachment(conn, body, file_headers, fullname)

          {:error, reason} ->
            Logger.error("Error al descargar el adjunto tras redirección: #{inspect(reason)}")
            conn |> send_resp(404, "File not found")
        end

      _ ->
        Logger.error("Redirección sin ubicación válida.")
        conn |> send_resp(404, "File not found")
    end
  end

  defp preview_attachment(conn, body, file_headers, fullname) do
    content_type = get_content_type(file_headers)

    conn
    |> put_resp_content_type(content_type)
    |> put_resp_header("content-disposition", "inline; filename=#{fullname}")
    |> send_resp(200, body)
  end

  defp get_content_type(headers) do
    case List.keyfind(headers, "content-type", 0) do
      {"content-type", content_type} -> content_type
      _ -> "application/octet-stream"
    end
  end

  defp content_type_for(format) do
    case String.downcase(format || "") do
      "pdf" -> "application/pdf"
      "jpg" -> "image/jpeg"
      "jpeg" -> "image/jpeg"
      "png" -> "image/png"
      _ -> "application/octet-stream"
    end
  end
end
