import { describe, expect, it } from "vitest";
import { authed, login, setup } from "../helpers.js";

/** LINE Harness / X Harness 連携アダプターのテスト */
describe("SNSハーネス連携", () => {
  async function seedHookWithCampaign() {
    const { app, store } = setup({ cfg: { dryRun: false } });
    const cookie = await login(app);
    const list = (
      await (
        await app.request("/api/lists", authed(cookie, { name: "SNS連携リスト" }))
      ).json() as any
    ).list;
    const camp = (
      await (
        await app.request(
          "/api/campaigns",
          authed(cookie, { name: "SNS→メール講座", list_id: list.id }),
        )
      ).json() as any
    ).campaign;
    await app.request(
      `/api/campaigns/${camp.id}/steps`,
      authed(cookie, {
        step_order: 0,
        delay_minutes: 0,
        subject: "ようこそ",
        body_html: "<p>SNSからの登録ありがとうございます</p>",
      }),
    );
    const hook = (
      await (
        await app.request(
          "/api/hooks",
          authed(cookie, {
            list_id: list.id,
            campaign_id: camp.id,
            name: "LINE→メール",
          }),
        )
      ).json() as any
    ).hook;
    return { app, store, cookie, list, camp, hook };
  }

  describe("LINE Harness → Mail Studio (/api/integrations/line/:token)", () => {
    it("form_submitted でメール付きならintake→enroll成功", async () => {
      const { app, store, hook, list, camp } = await seedHookWithCampaign();
      const res = await app.request(`/api/integrations/line/${hook.token}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          event: "form_submitted",
          timestamp: Date.now(),
          data: {
            friendId: "U1234567890",
            email: "line-user@example.com",
            fields: { "名前": "LINE太郎" },
            eventData: { displayName: "LINE太郎" },
          },
        }),
      });
      expect(res.status).toBe(200);
      const json = await res.json() as any;
      expect(json.ok).toBe(true);
      expect(json.created).toBe(true);
      expect(json.enrolled).toBe(true);
      expect(json.source).toBe("line:form_submitted");

      // 購読者がリストに入っている
      const subs = await store.subscribersInList(list.id);
      expect(subs.length).toBe(1);
      expect(subs[0].email).toBe("line-user@example.com");

      // enrollmentが作られている
      const enrollments = await store.listEnrollments(camp.id);
      expect(enrollments.length).toBe(1);
    });

    it("fields.メールアドレス からも email を解決できる", async () => {
      const { app, hook } = await seedHookWithCampaign();
      const res = await app.request(`/api/integrations/line/${hook.token}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          event: "form_submitted",
          data: { fields: { "メールアドレス": "alt@example.com", "お名前": "代替" } },
        }),
      });
      expect(res.status).toBe(200);
      const json = await res.json() as any;
      expect(json.created).toBe(true);
    });

    it("email なしの LINE イベントは 400 + ヒント", async () => {
      const { app, hook } = await seedHookWithCampaign();
      const res = await app.request(`/api/integrations/line/${hook.token}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          event: "friend_add",
          data: { friendId: "U9999", eventData: { displayName: "友だち" } },
        }),
      });
      expect(res.status).toBe(400);
      const json = await res.json() as any;
      expect(json.hint).toContain("data.email");
    });

    it("無効トークンは 404", async () => {
      const { app } = await seedHookWithCampaign();
      const res = await app.request("/api/integrations/line/invalid-token", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ event: "test", data: { email: "x@x.com" } }),
      });
      expect(res.status).toBe(404);
    });

    it("同一メールの重複呼び出しは冪等", async () => {
      const { app, store, hook, list } = await seedHookWithCampaign();
      const body = {
        event: "form_submitted",
        data: { email: "dup@example.com", friendId: "U111" },
      };
      await app.request(`/api/integrations/line/${hook.token}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      const res2 = await app.request(`/api/integrations/line/${hook.token}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      const json2 = await res2.json() as any;
      expect(json2.created).toBe(false); // 重複登録なし
      expect(json2.enrolled).toBe(false); // 重複enrollなし
      expect((await store.subscribersInList(list.id)).length).toBe(1);
    });
  });

  describe("X Harness → Mail Studio (/api/integrations/x/:token)", () => {
    it("engagement_gate_cleared でメール付きならintake→enroll成功", async () => {
      const { app, store, hook, camp } = await seedHookWithCampaign();
      const res = await app.request(`/api/integrations/x/${hook.token}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          event: "engagement_gate_cleared",
          xUserId: "12345",
          xUsername: "x_user",
          email: "x-user@example.com",
          name: "X太郎",
        }),
      });
      expect(res.status).toBe(200);
      const json = await res.json() as any;
      expect(json.ok).toBe(true);
      expect(json.created).toBe(true);
      expect(json.enrolled).toBe(true);
      expect(json.source).toBe("x:engagement_gate_cleared");

      const subs = await store.listSubscribers();
      expect(subs.find((s: any) => s.email === "x-user@example.com")).toBeTruthy();
      expect((await store.listEnrollments(camp.id)).length).toBe(1);
    });

    it("xUsername が name として使われる（name 省略時）", async () => {
      const { app, store, hook } = await seedHookWithCampaign();
      await app.request(`/api/integrations/x/${hook.token}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          event: "step_complete",
          xUsername: "handle123",
          email: "handle@example.com",
        }),
      });
      const subs = await store.listSubscribers();
      expect(subs.find((s: any) => s.email === "handle@example.com")?.name).toBe(
        "handle123",
      );
    });

    it("email なしの X イベントは 400", async () => {
      const { app, hook } = await seedHookWithCampaign();
      const res = await app.request(`/api/integrations/x/${hook.token}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          event: "engagement_gate_cleared",
          xUserId: "99",
          xUsername: "no_email_user",
        }),
      });
      expect(res.status).toBe(400);
    });
  });

  describe("認証不要（外部からの受信）", () => {
    it("LINE/X アダプターは認証Cookie不要でアクセスできる", async () => {
      const { app, hook } = await seedHookWithCampaign();
      // Cookie なしでアクセス（外部ハーネスからの受信を想定）
      const res = await app.request(`/api/integrations/line/${hook.token}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          event: "form_submitted",
          data: { email: "no-auth@test.com" },
        }),
      });
      expect(res.status).toBe(200);
    });
  });
});
