import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { ApiError } from "../../data/live/http";
import { KEYS } from "../../data/mock/keys";
import { I18nProvider } from "../../i18n/I18nContext";
import { translate } from "../../lib/i18n/messages";
import type { ServiceProfile } from "../../domain/types";
import { ServiceProfileForm } from "./ServiceProfileForm";

const t = (key: string, params?: Record<string, string | number>) => translate("zh-TW", key, params);

type SavePatch = Omit<ServiceProfile, "exists" | "updated_at">;

const backend = vi.hoisted(() => ({
  stored: null as ServiceProfile | null,
  saves: [] as SavePatch[],
  saveError: null as unknown,
}));

vi.mock("../../data/DataContext", () => {
  // repos 必須是同一個物件：元件用它當 effect 依賴，每次 render 換新的會抓不停。
  const repos = {
    radar: {
      getServiceProfile: async (): Promise<ServiceProfile> =>
        backend.stored ?? {
          exists: false,
          services: [],
          cases: [],
          forbidden: [],
          faq: [],
          service_areas: [],
          remote_ok: false,
        },
      saveServiceProfile: async (patch: SavePatch): Promise<ServiceProfile> => {
        backend.saves.push(patch);
        if (backend.saveError) throw backend.saveError;
        backend.stored = { ...patch, exists: true, updated_at: 1_700_000_000_000_000_000 };
        return backend.stored;
      },
    },
  };
  return { useRepos: () => repos };
});

function renderForm() {
  return render(
    <I18nProvider>
      <ServiceProfileForm />
    </I18nProvider>,
  );
}

/**
 * label 是包住 input 的 <label>；hint 也在同一個 label 裡，
 * 所以可及名稱是「標題＋提示」，只能做部分比對。
 */
function field(label: string): HTMLElement {
  return screen.getByLabelText(label, { exact: false });
}

beforeEach(() => {
  localStorage.setItem(
    KEYS.uiPrefs,
    JSON.stringify({ locale: "zh-TW", currency: "TWD", theme: "system" }),
  );
  backend.stored = null;
  backend.saves = [];
  backend.saveError = null;
});

describe("ServiceProfileForm", () => {
  it("尚未建立時給引導文案，而不是假裝已有檔案", async () => {
    renderForm();
    expect(await screen.findByText(t("radar.profile.firstTimeHint"))).toBeTruthy();
    expect(screen.queryByText(t("radar.profile.subtitle"))).toBeNull();
  });

  it("填寫→存檔→重載，欄位與後端一致", async () => {
    const first = renderForm();
    await screen.findByText(t("radar.profile.firstTimeHint"));

    fireEvent.change(field(t("radar.profile.serviceName")), {
      target: { value: " 室內設計丈量規劃 " },
    });
    fireEvent.change(field(t("radar.profile.priceMin")), { target: { value: "8000" } });
    fireEvent.change(field(t("radar.profile.priceMax")), { target: { value: "25000" } });
    fireEvent.change(field(t("radar.profile.forbidden")), {
      target: { value: "保證\n  最便宜  \n\n" },
    });
    fireEvent.change(field(t("radar.profile.availability")), { target: { value: "兩週內可開工" } });
    fireEvent.change(field(t("radar.profile.toneNote")), { target: { value: "講話直接" } });
    fireEvent.click(screen.getByRole("button", { name: "臺北市" }));
    fireEvent.click(screen.getByLabelText(t("radar.profile.remoteOk"), { selector: "input" }));

    fireEvent.click(screen.getByRole("button", { name: t("common.save") }));

    await screen.findByText(t("radar.profile.saved"));
    expect(backend.saves).toHaveLength(1);
    expect(backend.saves[0]).toEqual({
      services: [{ name: "室內設計丈量規劃", price_min: 8000, price_max: 25000, currency: "TWD" }],
      cases: [],
      faq: [],
      forbidden: ["保證", "最便宜"],
      service_areas: ["TPE"],
      remote_ok: true,
      availability: "兩週內可開工",
      tone_note: "講話直接",
    });

    // 重載：新掛一次元件，讀到的必須是剛存下去的內容
    first.unmount();
    renderForm();
    await screen.findByText(t("radar.profile.subtitle"));
    expect((field(t("radar.profile.serviceName")) as HTMLInputElement).value).toBe("室內設計丈量規劃");
    expect((field(t("radar.profile.priceMin")) as HTMLInputElement).value).toBe("8000");
    expect((field(t("radar.profile.forbidden")) as HTMLTextAreaElement).value).toBe("保證\n最便宜");
    expect((field(t("radar.profile.availability")) as HTMLTextAreaElement).value).toBe("兩週內可開工");
    expect(screen.getByRole("button", { name: "臺北市" }).getAttribute("aria-pressed")).toBe("true");
    expect(
      (screen.getByLabelText(t("radar.profile.remoteOk"), { selector: "input" }) as HTMLInputElement)
        .checked,
    ).toBe(true);
  });

  it("價格留空代表面議，不會被送成 0", async () => {
    renderForm();
    await screen.findByText(t("radar.profile.firstTimeHint"));
    fireEvent.change(field(t("radar.profile.serviceName")), { target: { value: "諮詢" } });
    fireEvent.click(screen.getByRole("button", { name: t("common.save") }));

    await waitFor(() => expect(backend.saves).toHaveLength(1));
    expect(backend.saves[0].services[0].price_min).toBeUndefined();
    expect(backend.saves[0].services[0].price_max).toBeUndefined();
  });

  it("驗證錯誤照後端 message 顯示，不吞成通用句", async () => {
    backend.saveError = new ApiError(
      "services[0].name required",
      400100,
      400,
      undefined,
      "services[0].name required",
    );
    renderForm();
    await screen.findByText(t("radar.profile.firstTimeHint"));
    fireEvent.change(field(t("radar.profile.serviceName")), { target: { value: "諮詢" } });
    fireEvent.click(screen.getByRole("button", { name: t("common.save") }));

    const banner = await screen.findByRole("alert");
    expect(banner.textContent).toContain("services[0].name required");
    expect(screen.queryByText(t("radar.profile.saved"))).toBeNull();
  });
});
