import { beforeEach, describe, expect, it, vi } from "vitest";

const { mkdirMock, appendFileMock } = vi.hoisted(() => ({
  mkdirMock: vi.fn(),
  appendFileMock: vi.fn(),
}));

vi.mock("node:fs/promises", () => ({
  mkdir: mkdirMock,
  appendFile: appendFileMock,
  readFile: vi.fn(),
}));

vi.mock("@/lib/email", () => ({
  sendLeadAlert: vi.fn(),
  sendProspectAck: vi.fn(),
  sendCallbackAlert: vi.fn(),
  sendNewsletterNotify: vi.fn(),
  getReceivers: vi.fn(() => []),
  checkSmtp: vi.fn(),
  countRecentMailFailures: vi.fn(),
}));

import {
  requestCallback,
  submitLead,
  subscribeNewsletter,
} from "./actions";
import {
  sendCallbackAlert,
  sendLeadAlert,
  sendNewsletterNotify,
  sendProspectAck,
} from "@/lib/email";

function form(entries: Record<string, string | string[]>) {
  const fd = new FormData();
  for (const [k, v] of Object.entries(entries)) {
    if (Array.isArray(v)) v.forEach((x) => fd.append(k, x));
    else fd.set(k, v);
  }
  return fd;
}

const LEAD = {
  name: "Jayveer Thakur",
  company: "Acme Manufacturing",
  email: "jay@acme.com",
  phone: "+919818993537",
  phoneCountry: "IN",
  industry: "Manufacturing",
  companySize: "21-100 employees",
  consent: "on",
  website: "",
};

const CALLBACK = {
  name: "Jayveer Thakur",
  phone: "+919818993537",
  phoneCountry: "IN",
  email: "jay@acme.com",
  industry: "Manufacturing",
  slot: "As soon as possible",
  consent: "on",
  website: "",
};

beforeEach(() => {
  vi.clearAllMocks();
});

describe("submitLead", () => {
  it("persists, notifies sales + prospect, and returns success", async () => {
    const state = await submitLead(
      { ok: false, message: "" },
      form({ ...LEAD, interests: ["Production", "Inventory"] })
    );
    expect(state.ok).toBe(true);

    // file row contains the lead fields
    expect(appendFileMock).toHaveBeenCalledTimes(1);
    const [, row] = appendFileMock.mock.calls[0];
    const saved = JSON.parse(String(row));
    expect(saved.name).toBe("Jayveer Thakur");
    expect(saved.email).toBe("jay@acme.com");
    expect(saved.interests).toEqual(["Production", "Inventory"]);

    // sales alert carries contact + reply path, prospect gets ack
    expect(sendLeadAlert).toHaveBeenCalledTimes(1);
    expect(sendLeadAlert).toHaveBeenCalledWith(
      expect.objectContaining({ email: "jay@acme.com", company: "Acme Manufacturing" })
    );
    expect(sendProspectAck).toHaveBeenCalledWith(
      "jay@acme.com",
      "Jayveer Thakur",
      ["Production", "Inventory"]
    );
  });

  it("rejects invalid email with field errors and zero side effects", async () => {
    const state = await submitLead(
      { ok: false, message: "" },
      form({ ...LEAD, email: "bad" })
    );
    expect(state.ok).toBe(false);
    expect(state.errors?.email).toBeTruthy();
    expect(appendFileMock).not.toHaveBeenCalled();
    expect(sendLeadAlert).not.toHaveBeenCalled();
    expect(sendProspectAck).not.toHaveBeenCalled();
  });

  it("honeypot returns fake success with zero side effects", async () => {
    const state = await submitLead(
      { ok: false, message: "" },
      form({ ...LEAD, website: "bot" })
    );
    expect(state.ok).toBe(true);
    expect(appendFileMock).not.toHaveBeenCalled();
    expect(sendLeadAlert).not.toHaveBeenCalled();
  });
});

describe("requestCallback", () => {
  it("saves callback, alerts sales, returns personal message", async () => {
    const state = await requestCallback(
      { ok: false, message: "" },
      form(CALLBACK)
    );
    expect(state.ok).toBe(true);
    expect(state.message).toMatch(/Jayveer/);
    expect(sendCallbackAlert).toHaveBeenCalledWith({
      name: "Jayveer Thakur",
      phone: "+919818993537",
      phoneCountry: "IN",
      email: "jay@acme.com",
      industry: "Manufacturing",
      slot: "As soon as possible",
    });
  });

  it("rejects short Indian numbers naming the rule", async () => {
    const state = await requestCallback(
      { ok: false, message: "" },
      form({ ...CALLBACK, phone: "+911234567" })
    );
    expect(state.ok).toBe(false);
    expect(state.errors?.phone).toMatch(/10 digits/);
    expect(sendCallbackAlert).not.toHaveBeenCalled();
  });
});

describe("subscribeNewsletter", () => {
  it("normalizes email to lowercase and notifies", async () => {
    const state = await subscribeNewsletter(
      { ok: false, message: "" },
      form({ email: "Jay@Acme.COM" })
    );
    expect(state.ok).toBe(true);
    const [, row] = appendFileMock.mock.calls[0];
    expect(JSON.parse(String(row)).email).toBe("jay@acme.com");
    expect(sendNewsletterNotify).toHaveBeenCalledWith("jay@acme.com");
  });

  it("rejects malformed email with no side effects", async () => {
    const state = await subscribeNewsletter(
      { ok: false, message: "" },
      form({ email: "nope" })
    );
    expect(state.ok).toBe(false);
    expect(appendFileMock).not.toHaveBeenCalled();
    expect(sendNewsletterNotify).not.toHaveBeenCalled();
  });
});
