defmodule MvWeb.ImportLiveTest do
  @moduledoc """
  Tests for Import LiveView: authorization (business rule), CSV import integration,
  and minimal UI smoke tests. CSV parsing/validation logic is covered by
  Mv.Membership.Import.MemberCSVTest; here we verify access control and end-to-end outcomes.
  """
  use MvWeb.ConnCase, async: true
  import Phoenix.LiveViewTest

  alias Mv.Membership

  defp put_locale_en(conn), do: Plug.Conn.put_session(conn, "locale", "en")

  defp upload_csv_file(view, csv_content, filename \\ "test_import.csv") do
    view
    |> file_input("#csv-upload-form", :csv_file, [
      %{
        last_modified: System.system_time(:second),
        name: filename,
        content: csv_content,
        size: byte_size(csv_content),
        type: "text/csv"
      }
    ])
    |> render_upload(filename)
  end

  defp submit_import(view), do: view |> form("#csv-upload-form", %{}) |> render_submit()

  defp wait_for_import_completion, do: Process.sleep(1000)

  # ---------- Business logic: Authorization ----------
  describe "Authorization" do
    test "non-admin user cannot access import page and sees permission error", %{
      conn: conn
    } do
      member_user = Mv.Fixtures.user_with_role_fixture("own_data")

      conn =
        conn
        |> MvWeb.ConnCase.conn_with_password_user(member_user)
        |> put_locale_en()

      assert {:error, {:redirect, %{to: redirect_path, flash: %{"error" => msg}}}} =
               live(conn, ~p"/admin/import")

      assert redirect_path =~ "/users/"
      assert msg =~ "don't have permission"
    end

    test "admin user can access page and run import", %{conn: conn} do
      conn = put_locale_en(conn)

      csv_content =
        Path.join([__DIR__, "..", "..", "fixtures", "valid_member_import.csv"])
        |> File.read!()

      {:ok, view, _html} = live(conn, ~p"/admin/import")
      upload_csv_file(view, csv_content)
      submit_import(view)
      wait_for_import_completion()

      assert has_element?(view, "[data-testid='import-results-panel']")
      assert has_element?(view, "[data-testid='import-summary']")
      html = render(view)
      refute html =~ "Import aborted"
      assert html =~ "Successfully inserted"

      # Business outcome: two members from fixture were created
      system_actor = Mv.Helpers.SystemActor.get_system_actor()
      {:ok, members} = Membership.list_members(actor: system_actor)

      imported =
        Enum.filter(members, fn m ->
          m.email in ["alice.smith@example.com", "bob.johnson@example.com"]
        end)

      assert length(imported) == 2
    end
  end

  # ---------- Business logic: Import behaviour (integration) ----------
  describe "CSV Import - integration" do
    setup %{conn: conn} do
      admin_user = Mv.Fixtures.user_with_role_fixture("admin")

      conn =
        conn
        |> MvWeb.ConnCase.conn_with_password_user(admin_user)
        |> put_locale_en()

      valid_csv =
        Path.join([__DIR__, "..", "..", "fixtures", "valid_member_import.csv"])
        |> File.read!()

      invalid_csv =
        Path.join([__DIR__, "..", "..", "fixtures", "invalid_member_import.csv"])
        |> File.read!()

      unknown_cf_csv =
        Path.join([__DIR__, "..", "..", "fixtures", "csv_with_unknown_custom_field.csv"])
        |> File.read!()

      {:ok,
       conn: conn,
       valid_csv: valid_csv,
       invalid_csv: invalid_csv,
       unknown_custom_field_csv: unknown_cf_csv}
    end

    test "invalid CSV shows user-friendly prepare error", %{conn: conn} do
      {:ok, view, _html} = live(conn, ~p"/admin/import")
      upload_csv_file(view, "invalid_header\nincomplete_row", "invalid.csv")
      submit_import(view)
      html = render(view)
      assert html =~ "Failed to prepare CSV import"
    end

    test "invalid rows show errors with correct CSV line numbers", %{
      conn: conn,
      invalid_csv: csv_content
    } do
      {:ok, view, _html} = live(conn, ~p"/admin/import")
      upload_csv_file(view, csv_content, "invalid_import.csv")
      submit_import(view)
      wait_for_import_completion()

      assert has_element?(view, "[data-testid='import-results-panel']")
      assert has_element?(view, "[data-testid='import-error-list']")
      html = render(view)
      assert html =~ "Failed"
      # Fixture has invalid email on line 2 and missing email on line 3
      assert html =~ "Line 2"
      assert html =~ "Line 3"
    end

    test "error list is capped and truncation message is shown", %{conn: conn} do
      {:ok, view, _html} = live(conn, ~p"/admin/import")
      header = "first_name;last_name;email;country;city;street;postal_code\n"

      invalid_rows =
        for i <- 1..100, do: "Row#{i};Last#{i};;Country#{i};City#{i};Street#{i};12345\n"

      upload_csv_file(view, header <> Enum.join(invalid_rows), "large_invalid.csv")
      submit_import(view)
      wait_for_import_completion()

      assert has_element?(view, "[data-testid='import-results-panel']")
      assert has_element?(view, "[data-testid='import-error-list']")
      html = render(view)
      assert html =~ "100"
      assert html =~ "Error list truncated"
    end

    test "row limit is enforced (1001 rows rejected)", %{conn: conn} do
      {:ok, view, _html} = live(conn, ~p"/admin/import")
      header = "first_name;last_name;email;country;city;street;postal_code\n"

      rows =
        for i <- 1..1001 do
          "Row#{i};Last#{i};email#{i}@example.com;Country#{i};City#{i};Street#{i};12345\n"
        end

      upload_csv_file(view, header <> Enum.join(rows), "too_many_rows.csv")
      submit_import(view)
      html = render(view)
      assert html =~ "exceeds"
    end

    test "BOM and semicolon delimiter are accepted", %{conn: conn} do
      {:ok, view, _html} = live(conn, ~p"/admin/import")

      csv_content =
        Path.join([__DIR__, "..", "..", "fixtures", "csv_with_bom_semicolon.csv"])
        |> File.read!()

      upload_csv_file(view, csv_content, "bom_import.csv")
      submit_import(view)
      wait_for_import_completion()

      assert has_element?(view, "[data-testid='import-results-panel']")
      html = render(view)
      assert html =~ "Successfully inserted"
      refute html =~ "BOM"
    end

    test "physical line numbers in errors (empty line does not shift numbering)", %{
      conn: conn
    } do
      {:ok, view, _html} = live(conn, ~p"/admin/import")

      csv_content =
        Path.join([__DIR__, "..", "..", "fixtures", "csv_with_empty_lines.csv"])
        |> File.read!()

      upload_csv_file(view, csv_content, "empty_lines.csv")
      submit_import(view)
      wait_for_import_completion()

      assert has_element?(view, "[data-testid='import-error-list']")
      html = render(view)
      # Invalid row is on physical line 4 (header, valid row, empty line, then invalid)
      assert html =~ "Line 4"
    end

    test "unknown custom field column produces warnings", %{
      conn: conn,
      unknown_custom_field_csv: csv_content
    } do
      {:ok, view, _html} = live(conn, ~p"/admin/import")
      upload_csv_file(view, csv_content, "unknown_custom.csv")
      submit_import(view)
      wait_for_import_completion()

      assert has_element?(view, "[data-testid='import-results-panel']")
      assert has_element?(view, "[data-testid='import-warnings']")
      html = render(view)
      assert html =~ "Warnings"
    end
  end

  # ---------- UI (smoke / framework): tagged for exclusion from fast CI ----------
  describe "Import page UI" do
    @describetag :ui
    setup %{conn: conn} do
      admin_user = Mv.Fixtures.user_with_role_fixture("admin")

      conn =
        conn
        |> MvWeb.ConnCase.conn_with_password_user(admin_user)
        |> put_locale_en()

      {:ok, conn: conn}
    end

    test "page loads and shows import form", %{conn: conn} do
      {:ok, view, _html} = live(conn, ~p"/admin/import")
      assert has_element?(view, "[data-testid='import-page']")
      assert has_element?(view, "[data-testid='csv-upload-form']")
      assert has_element?(view, "[data-testid='start-import-button']")
      assert has_element?(view, "[data-testid='custom-fields-link']")
    end

    test "template links and file input are present", %{conn: conn} do
      {:ok, view, _html} = live(conn, ~p"/admin/import")
      assert has_element?(view, "a[href*='/templates/member_import_en.csv']")
      assert has_element?(view, "a[href*='/templates/member_import_de.csv']")
      assert has_element?(view, "label[for='csv_file']")
      assert has_element?(view, "#csv_file_help")
      assert has_element?(view, "[data-testid='csv-upload-form'] input[type='file']")
    end

    test "after successful import, progress container has aria-live", %{conn: conn} do
      csv_content =
        Path.join([__DIR__, "..", "..", "fixtures", "valid_member_import.csv"])
        |> File.read!()

      {:ok, view, _html} = live(conn, ~p"/admin/import")
      upload_csv_file(view, csv_content)
      submit_import(view)
      wait_for_import_completion()
      assert has_element?(view, "[data-testid='import-progress-container']")
      html = render(view)
      assert html =~ "aria-live"
    end
  end

  # Skip: LiveView test harness does not reliably support empty/minimal file uploads.
  # See docs/csv-member-import-v1.md (Issue #9).
  @tag :skip
  test "empty CSV shows error", %{conn: conn} do
    conn = put_locale_en(conn)
    {:ok, view, _html} = live(conn, ~p"/admin/import")
    upload_csv_file(view, " ", "empty.csv")
    submit_import(view)
    html = render(view)
    assert html =~ "Failed to prepare"
  end
end
