defmodule BerrypodWeb.Admin.Backup do
  use BerrypodWeb, :live_view

  alias Berrypod.Backup

  @impl true
  def mount(_params, _session, socket) do
    stats = Backup.get_stats()
    backups = Backup.list_backups()

    {:ok,
     socket
     |> assign(:page_title, "Backup")
     |> assign(:stats, stats)
     |> assign(:backups, backups)
     |> assign(:create_backup_status, :idle)
     |> assign(:uploaded_backup, nil)
     |> assign(:upload_error, nil)
     |> assign(:confirming_restore, false)
     |> assign(:restoring, false)
     |> assign(:confirming_history_restore, nil)
     |> assign(:confirming_delete, nil)
     |> assign(:show_tables, false)
     |> allow_upload(:backup,
       accept: :any,
       max_entries: 1,
       max_file_size: 500_000_000
     )}
  end

  @impl true
  def handle_event("refresh_stats", _params, socket) do
    stats = Backup.get_stats()
    backups = Backup.list_backups()
    {:noreply, socket |> assign(:stats, stats) |> assign(:backups, backups)}
  end

  def handle_event("toggle_tables", _params, socket) do
    {:noreply, assign(socket, :show_tables, !socket.assigns.show_tables)}
  end

  def handle_event("create_backup", _params, socket) do
    if socket.assigns.create_backup_status == :saving do
      {:noreply, socket}
    else
      send(self(), :do_create_backup)
      {:noreply, assign(socket, :create_backup_status, :saving)}
    end
  end

  def handle_event("download_history_backup", %{"filename" => filename}, socket) do
    path = Path.join(Backup.backup_dir(), filename)

    if File.exists?(path) do
      data = File.read!(path)

      {:noreply,
       socket
       |> push_event("download", %{
         filename: filename,
         content: Base.encode64(data),
         content_type: "application/octet-stream"
       })}
    else
      {:noreply, put_flash(socket, :error, "Backup file not found")}
    end
  end

  def handle_event("validate_upload", _params, socket) do
    {:noreply, socket}
  end

  def handle_event("upload_backup", _params, socket) do
    [result] =
      consume_uploaded_entries(socket, :backup, fn %{path: path}, _entry ->
        # Copy to temp location since consume deletes the original
        temp_path = Path.join(System.tmp_dir!(), "berrypod-restore-#{System.unique_integer()}.db")
        File.cp!(path, temp_path)

        case Backup.validate_backup(temp_path) do
          {:ok, backup_stats} ->
            # Use actual file size instead of internal page calculation
            file_size = File.stat!(temp_path).size
            {:ok, {:ok, temp_path, Map.put(backup_stats, :file_size, file_size)}}

          {:error, reason} ->
            File.rm(temp_path)
            {:ok, {:error, reason}}
        end
      end)

    case result do
      {:ok, path, backup_stats} ->
        {:noreply,
         socket
         |> assign(:uploaded_backup, %{path: path, stats: backup_stats})
         |> assign(:upload_error, nil)}

      {:error, :invalid_key} ->
        {:noreply,
         assign(
           socket,
           :upload_error,
           "Wrong encryption key — this backup was created with a different key"
         )}

      {:error, reason} ->
        {:noreply, assign(socket, :upload_error, "Invalid backup file: #{inspect(reason)}")}
    end
  end

  def handle_event("cancel_restore", _params, socket) do
    # Clean up temp file
    if socket.assigns.uploaded_backup do
      File.rm(socket.assigns.uploaded_backup.path)
    end

    {:noreply,
     socket
     |> assign(:uploaded_backup, nil)
     |> assign(:confirming_restore, false)}
  end

  def handle_event("confirm_restore", _params, socket) do
    {:noreply, assign(socket, :confirming_restore, true)}
  end

  def handle_event("execute_restore", _params, socket) do
    # Show loading state immediately, then do the restore async
    send(self(), :do_restore)
    {:noreply, assign(socket, :restoring, true)}
  end

  # Backup history actions
  def handle_event("confirm_history_restore", %{"filename" => filename}, socket) do
    {:noreply, assign(socket, :confirming_history_restore, filename)}
  end

  def handle_event("cancel_history_restore", _params, socket) do
    {:noreply, assign(socket, :confirming_history_restore, nil)}
  end

  def handle_event("execute_history_restore", %{"filename" => filename}, socket) do
    send(self(), {:do_history_restore, filename})
    {:noreply, socket |> assign(:restoring, true) |> assign(:confirming_history_restore, nil)}
  end

  def handle_event("confirm_delete", %{"filename" => filename}, socket) do
    {:noreply, assign(socket, :confirming_delete, filename)}
  end

  def handle_event("cancel_delete", _params, socket) do
    {:noreply, assign(socket, :confirming_delete, nil)}
  end

  def handle_event("execute_delete", %{"filename" => filename}, socket) do
    case Backup.delete_backup(filename) do
      :ok ->
        {:noreply,
         socket
         |> assign(:confirming_delete, nil)
         |> assign(:backups, Backup.list_backups())
         |> put_flash(:info, "Backup deleted")}

      {:error, _} ->
        {:noreply,
         socket
         |> assign(:confirming_delete, nil)
         |> put_flash(:error, "Failed to delete backup")}
    end
  end

  @impl true
  def handle_info(:do_create_backup, socket) do
    case Backup.create_backup() do
      {:ok, _path} ->
        {:noreply,
         socket
         |> assign(:backups, Backup.list_backups())
         |> assign(:create_backup_status, :saved)}

      {:error, error} ->
        {:noreply,
         socket
         |> assign(:create_backup_status, :error)
         |> put_flash(:error, "Failed to create backup: #{inspect(error)}")}
    end
  end

  def handle_info(:do_restore, socket) do
    backup_path = socket.assigns.uploaded_backup.path

    case Backup.restore_backup(backup_path) do
      :ok ->
        {:noreply,
         socket
         |> assign(:uploaded_backup, nil)
         |> assign(:confirming_restore, false)
         |> assign(:restoring, false)
         |> assign(:stats, Backup.get_stats())
         |> assign(:backups, Backup.list_backups())
         |> put_flash(:info, "Database restored successfully")}

      {:error, {:schema_mismatch, backup_version, current_version}} ->
        {:noreply,
         socket
         |> assign(:confirming_restore, false)
         |> assign(:restoring, false)
         |> put_flash(
           :error,
           "Schema version mismatch: backup is #{backup_version}, current is #{current_version}. " <>
             "Backups can only be restored to a database with the same schema version."
         )}

      {:error, reason} ->
        {:noreply,
         socket
         |> assign(:confirming_restore, false)
         |> assign(:restoring, false)
         |> put_flash(:error, "Restore failed: #{inspect(reason)}")}
    end
  end

  def handle_info({:do_history_restore, filename}, socket) do
    path = Path.join(Backup.backup_dir(), filename)

    case Backup.restore_backup(path) do
      :ok ->
        {:noreply,
         socket
         |> assign(:restoring, false)
         |> assign(:stats, Backup.get_stats())
         |> assign(:backups, Backup.list_backups())
         |> put_flash(:info, "Database restored from #{filename}")}

      {:error, {:schema_mismatch, backup_version, current_version}} ->
        {:noreply,
         socket
         |> assign(:restoring, false)
         |> put_flash(
           :error,
           "Schema version mismatch: backup is #{backup_version}, current is #{current_version}."
         )}

      {:error, reason} ->
        {:noreply,
         socket
         |> assign(:restoring, false)
         |> put_flash(:error, "Restore failed: #{inspect(reason)}")}
    end
  end

  @impl true
  def render(assigns) do
    ~H"""
    <div class="admin-backup" phx-hook="Download" id="backup-page">
      <.header>
        Backup
      </.header>

      <%!-- Database status --%>
      <section class="admin-section">
        <div class="admin-section-header">
          <h2 class="admin-section-title">Database</h2>
          <%= if @stats.encryption_status do %>
            <.status_pill color="green">
              <.icon name="hero-lock-closed-mini" class="size-3" /> Encrypted
            </.status_pill>
          <% else %>
            <.status_pill color="amber">
              <.icon name="hero-lock-open-mini" class="size-3" /> Not encrypted
            </.status_pill>
          <% end %>
        </div>
        <p class="admin-section-desc">
          {Backup.format_size(@stats.total_size)} total · {length(@stats.tables)} tables · {@stats.key_counts[
            "products"
          ] || 0} products · {@stats.key_counts["orders"] || 0} orders · {@stats.key_counts["images"] ||
            0} images
        </p>
        <div class="admin-section-body">
          <button
            type="button"
            phx-click="toggle_tables"
            class="admin-link"
          >
            <%= if @show_tables do %>
              <.icon name="hero-chevron-up-mini" class="size-4" /> Hide table details
            <% else %>
              <.icon name="hero-chevron-down-mini" class="size-4" /> Show table details
            <% end %>
          </button>
        </div>

        <%= if @show_tables do %>
          <div class="backup-tables">
            <table class="admin-table admin-table-compact">
              <thead>
                <tr>
                  <th>Table</th>
                  <th class="text-right">Rows</th>
                  <th class="text-right">Size</th>
                </tr>
              </thead>
              <tbody>
                <tr :for={table <- @stats.tables}>
                  <td>{table.name}</td>
                  <td class="text-right tabular-nums">{table.rows}</td>
                  <td class="text-right tabular-nums">{Backup.format_size(table.size)}</td>
                </tr>
              </tbody>
            </table>
          </div>
        <% end %>
      </section>

      <%!-- Create backup --%>
      <section class="admin-section">
        <div class="admin-section-header">
          <h2 class="admin-section-title">Create backup</h2>
          <.status_pill color="zinc">{length(@backups)} saved</.status_pill>
        </div>
        <p class="admin-section-desc">
          Creates an encrypted snapshot of your database. Backups are stored locally and the last 5 are kept automatically.
        </p>

        <div class="admin-section-body">
          <div class="backup-actions">
            <button
              type="button"
              phx-click="create_backup"
              phx-throttle="1000"
              class="admin-btn admin-btn-primary admin-btn-sm"
              disabled={@create_backup_status == :saving}
            >
              <.icon name="hero-plus-mini" class="size-4" /> Create backup
            </button>
            <.inline_feedback status={@create_backup_status} />
          </div>
        </div>
      </section>

      <%!-- Backup history --%>
      <%= if @backups != [] do %>
        <section class="admin-section">
          <h2 class="admin-section-title">Saved backups</h2>

          <%= if @restoring do %>
            <div class="backup-progress">
              <.icon name="hero-arrow-path" class="size-5 animate-spin" />
              <div>
                <p class="backup-progress-text">Restoring database...</p>
                <p class="backup-progress-hint">This may take a few seconds.</p>
              </div>
            </div>
          <% else %>
            <div class="backup-list">
              <%= for backup <- @backups do %>
                <div class="backup-item">
                  <div class="backup-item-info">
                    <span class="backup-item-date">{format_backup_date(backup.created_at)}</span>
                    <span class="backup-item-meta">
                      {Backup.format_size(backup.size)}
                      <%= if backup.type == :pre_restore do %>
                        · auto-saved before restore
                      <% end %>
                    </span>
                  </div>

                  <div class="backup-item-actions">
                    <%= if @confirming_history_restore == backup.filename do %>
                      <span class="backup-item-confirm">Replace current database?</span>
                      <button
                        type="button"
                        class="admin-btn admin-btn-danger admin-btn-sm"
                        phx-click="execute_history_restore"
                        phx-value-filename={backup.filename}
                      >
                        Restore
                      </button>
                      <button
                        type="button"
                        class="admin-btn admin-btn-outline admin-btn-sm"
                        phx-click="cancel_history_restore"
                      >
                        Cancel
                      </button>
                    <% else %>
                      <%= if @confirming_delete == backup.filename do %>
                        <span class="backup-item-confirm">Delete this backup?</span>
                        <button
                          type="button"
                          class="admin-btn admin-btn-danger admin-btn-sm"
                          phx-click="execute_delete"
                          phx-value-filename={backup.filename}
                        >
                          Delete
                        </button>
                        <button
                          type="button"
                          class="admin-btn admin-btn-outline admin-btn-sm"
                          phx-click="cancel_delete"
                        >
                          Cancel
                        </button>
                      <% else %>
                        <button
                          type="button"
                          class="admin-btn admin-btn-outline admin-btn-sm"
                          phx-click="download_history_backup"
                          phx-value-filename={backup.filename}
                          title="Download"
                        >
                          <.icon name="hero-arrow-down-tray-mini" class="size-4" />
                        </button>
                        <button
                          type="button"
                          class="admin-btn admin-btn-outline admin-btn-sm"
                          phx-click="confirm_history_restore"
                          phx-value-filename={backup.filename}
                        >
                          Restore
                        </button>
                        <button
                          type="button"
                          class="admin-btn admin-btn-outline admin-btn-sm"
                          phx-click="confirm_delete"
                          phx-value-filename={backup.filename}
                          title="Delete"
                        >
                          <.icon name="hero-trash-mini" class="size-4" />
                        </button>
                      <% end %>
                    <% end %>
                  </div>
                </div>
              <% end %>
            </div>
          <% end %>
        </section>
      <% end %>

      <%!-- Restore from file --%>
      <section class="admin-section">
        <h2 class="admin-section-title">Restore from file</h2>
        <p class="admin-section-desc">
          Upload a backup file to restore. Must be encrypted with the same key as this database.
        </p>

        <%= if @upload_error do %>
          <p class="admin-error">{@upload_error}</p>
        <% end %>

        <%= if @uploaded_backup do %>
          <div class="backup-comparison">
            <div class="backup-comparison-grid">
              <div class="backup-comparison-col">
                <h4 class="backup-comparison-label">Current</h4>
                <dl class="backup-comparison-stats">
                  <div>
                    <dt>Size</dt>
                    <dd>{Backup.format_size(@stats.total_size)}</dd>
                  </div>
                  <div>
                    <dt>Products</dt>
                    <dd>{@stats.key_counts["products"] || 0}</dd>
                  </div>
                  <div>
                    <dt>Orders</dt>
                    <dd>{@stats.key_counts["orders"] || 0}</dd>
                  </div>
                  <div>
                    <dt>Images</dt>
                    <dd>{@stats.key_counts["images"] || 0}</dd>
                  </div>
                </dl>
              </div>
              <div class="backup-comparison-arrow">
                <.icon name="hero-arrow-right" class="size-5" />
              </div>
              <div class="backup-comparison-col">
                <h4 class="backup-comparison-label">Uploaded</h4>
                <dl class="backup-comparison-stats">
                  <div>
                    <dt>Size</dt>
                    <dd>{Backup.format_size(@uploaded_backup.stats.file_size)}</dd>
                  </div>
                  <div>
                    <dt>Products</dt>
                    <dd>{@uploaded_backup.stats.key_counts["products"] || 0}</dd>
                  </div>
                  <div>
                    <dt>Orders</dt>
                    <dd>{@uploaded_backup.stats.key_counts["orders"] || 0}</dd>
                  </div>
                  <div>
                    <dt>Images</dt>
                    <dd>{@uploaded_backup.stats.key_counts["images"] || 0}</dd>
                  </div>
                </dl>
              </div>
            </div>

            <%= if @uploaded_backup.stats.latest_migration == @stats.schema_version do %>
              <div class="backup-validation backup-validation-ok">
                <.icon name="hero-check-circle-mini" class="size-4" />
                <span>
                  Backup validated · Schema version {@uploaded_backup.stats.latest_migration}
                </span>
              </div>

              <%= if @restoring do %>
                <div class="backup-progress">
                  <.icon name="hero-arrow-path" class="size-5 animate-spin" />
                  <div>
                    <p class="backup-progress-text">Restoring database...</p>
                    <p class="backup-progress-hint">This may take a few seconds.</p>
                  </div>
                </div>
              <% else %>
                <%= if @confirming_restore do %>
                  <div class="backup-warning">
                    <p>
                      This will replace your current database. A backup will be saved automatically.
                    </p>
                    <div class="backup-actions">
                      <button
                        type="button"
                        class="admin-btn admin-btn-danger admin-btn-sm"
                        phx-click="execute_restore"
                      >
                        Replace database
                      </button>
                      <button
                        type="button"
                        class="admin-btn admin-btn-outline admin-btn-sm"
                        phx-click="cancel_restore"
                      >
                        Cancel
                      </button>
                    </div>
                  </div>
                <% else %>
                  <div class="backup-actions">
                    <button
                      type="button"
                      class="admin-btn admin-btn-primary admin-btn-sm"
                      phx-click="confirm_restore"
                    >
                      Restore this backup
                    </button>
                    <button
                      type="button"
                      class="admin-btn admin-btn-outline admin-btn-sm"
                      phx-click="cancel_restore"
                    >
                      Cancel
                    </button>
                  </div>
                <% end %>
              <% end %>
            <% else %>
              <div class="backup-validation backup-validation-error">
                <.icon name="hero-x-circle-mini" class="size-4" />
                <span>
                  Schema mismatch: backup is v{@uploaded_backup.stats.latest_migration},
                  current is v{@stats.schema_version}
                </span>
              </div>
              <div class="backup-actions">
                <button
                  type="button"
                  class="admin-btn admin-btn-outline admin-btn-sm"
                  phx-click="cancel_restore"
                >
                  Cancel
                </button>
              </div>
            <% end %>
          </div>
        <% else %>
          <form phx-submit="upload_backup" phx-change="validate_upload">
            <div class="backup-dropzone" phx-drop-target={@uploads.backup.ref}>
              <.live_file_input upload={@uploads.backup} class="sr-only" />
              <div class="backup-dropzone-content">
                <.icon name="hero-arrow-up-tray" class="size-6" />
                <p>
                  Drop a backup file here or
                  <label for={@uploads.backup.ref} class="backup-dropzone-link">browse</label>
                </p>
              </div>
            </div>

            <%= for entry <- @uploads.backup.entries do %>
              <div class="backup-upload-entry">
                <span>{entry.client_name}</span>
                <span class="tabular-nums">{Backup.format_size(entry.client_size)}</span>
                <progress value={entry.progress} max="100">{entry.progress}%</progress>
              </div>

              <%= for err <- upload_errors(@uploads.backup, entry) do %>
                <p class="admin-error">{upload_error_to_string(err)}</p>
              <% end %>
            <% end %>

            <%= if length(@uploads.backup.entries) > 0 do %>
              <div class="backup-actions">
                <button type="submit" class="admin-btn admin-btn-primary admin-btn-sm">
                  <.icon name="hero-arrow-up-tray-mini" class="size-4" /> Upload and validate
                </button>
              </div>
            <% end %>
          </form>
        <% end %>
      </section>
    </div>
    """
  end

  defp format_backup_date(nil), do: "unknown date"

  defp format_backup_date(datetime) do
    Calendar.strftime(datetime, "%d %b %Y, %H:%M")
  end

  defp upload_error_to_string(:too_large), do: "File is too large (max 500 MB)"
  defp upload_error_to_string(:too_many_files), do: "Only one file allowed"
  defp upload_error_to_string(err), do: "Upload error: #{inspect(err)}"

  attr :color, :string, default: "zinc"
  slot :inner_block, required: true

  defp status_pill(assigns) do
    modifier =
      case assigns.color do
        "green" -> "admin-status-pill-green"
        "amber" -> "admin-status-pill-amber"
        _ -> "admin-status-pill-zinc"
      end

    assigns = assign(assigns, :modifier, modifier)

    ~H"""
    <span class={["admin-status-pill", @modifier]}>
      {render_slot(@inner_block)}
    </span>
    """
  end
end
