import * as SecureStore from "expo-secure-store";
import {
  clearMobileConfig,
  getMobileConfig,
  getMobileConfigForBaseUrl,
  saveMobileConfig,
} from "../../src/core/mobileConfigService";

const validConfig = {
  app_name: "MissioFlow",
  primary_color: "#1E56A8",
  logo_url: "https://a.b/logo.png",
  tenant_id: 42,
  support_email: null,
  support_phone: null,
};

async function resetCache(): Promise<void> {
  // Le module maintient un cache au niveau module. clearMobileConfig le remet
  // a null (cf source) : c'est le meilleur point d'entree public pour garantir
  // un etat vierge entre tests.
  await clearMobileConfig();
  jest.clearAllMocks();
}

describe("mobileConfigService", () => {
  beforeEach(async () => {
    await resetCache();
  });

  describe("saveMobileConfig", () => {
    it("normalise le baseUrl, sanitize et stocke", async () => {
      const stored = await saveMobileConfig("https://a.b/api//", validConfig);
      expect(stored.base_url).toBe("https://a.b/api");
      expect(stored.config).toEqual(validConfig);
      expect(SecureStore.setItemAsync).toHaveBeenCalled();
    });

    it("logo_url trim et nullifie si vide", async () => {
      const stored = await saveMobileConfig("https://a.b", {
        ...validConfig,
        logo_url: null,
      });
      expect(stored.config.logo_url).toBeNull();
    });

    it("logo_url trim les espaces", async () => {
      const stored = await saveMobileConfig("https://a.b", {
        ...validConfig,
        logo_url: "  https://x  ",
      });
      expect(stored.config.logo_url).toBe("https://x");
    });

    it("support_email/phone trim et conserve", async () => {
      const stored = await saveMobileConfig("https://a.b", {
        ...validConfig,
        support_email: "  help@a.b  ",
        support_phone: "  +33 1 23  ",
      });
      expect(stored.config.support_email).toBe("help@a.b");
      expect(stored.config.support_phone).toBe("+33 1 23");
    });

    it("support_email/phone nullifie si vide ou absent", async () => {
      const stored = await saveMobileConfig("https://a.b", {
        ...validConfig,
        support_email: "   ",
        support_phone: undefined as unknown as string,
      });
      expect(stored.config.support_email).toBeNull();
      expect(stored.config.support_phone).toBeNull();
    });

    it("tenant_id fallback sur null quand undefined", async () => {
      const stored = await saveMobileConfig("https://a.b", {
        ...validConfig,
        tenant_id: null,
      });
      expect(stored.config.tenant_id).toBeNull();
    });

    it("rejette app_name manquant", async () => {
      await expect(
        saveMobileConfig("https://a.b", { ...validConfig, app_name: "" })
      ).rejects.toThrow(/app_name manquant/);
    });

    it("rejette primary_color undefined (fallback '')", async () => {
      await expect(
        saveMobileConfig("https://a.b", { ...validConfig, primary_color: undefined as unknown as string })
      ).rejects.toThrow(/hex #RRGGBB/);
    });

    it("rejette app_name undefined (fallback '')", async () => {
      await expect(
        saveMobileConfig("https://a.b", { ...validConfig, app_name: undefined as unknown as string })
      ).rejects.toThrow(/app_name manquant/);
    });

    it("rejette primary_color non hex", async () => {
      await expect(
        saveMobileConfig("https://a.b", { ...validConfig, primary_color: "blue" })
      ).rejects.toThrow(/hex #RRGGBB/);
    });
  });

  describe("getMobileConfig", () => {
    it("retourne le cache quand deja charge", async () => {
      await saveMobileConfig("https://a.b", validConfig);
      jest.clearAllMocks();
      const stored = await getMobileConfig();
      expect(stored?.base_url).toBe("https://a.b");
      expect(SecureStore.getItemAsync).not.toHaveBeenCalled();
    });

    it("charge depuis SecureStore puis mise en cache", async () => {
      (SecureStore.getItemAsync as jest.Mock).mockResolvedValue(
        JSON.stringify({
          base_url: "https://a.b/",
          config: validConfig,
          saved_at: "2026-01-01",
        })
      );
      const stored = await getMobileConfig();
      expect(stored?.base_url).toBe("https://a.b");
      expect(stored?.saved_at).toBe("2026-01-01");
    });

    it("retourne null si rien stocke", async () => {
      (SecureStore.getItemAsync as jest.Mock).mockResolvedValue(null);
      expect(await getMobileConfig()).toBeNull();
    });

    it("retourne null si JSON corrompu", async () => {
      (SecureStore.getItemAsync as jest.Mock).mockResolvedValue("{bad");
      expect(await getMobileConfig()).toBeNull();
    });

    it("retourne null si structure invalide", async () => {
      (SecureStore.getItemAsync as jest.Mock).mockResolvedValue(
        JSON.stringify({ base_url: 42, config: validConfig })
      );
      expect(await getMobileConfig()).toBeNull();
    });

    it("retourne null si config manquant", async () => {
      (SecureStore.getItemAsync as jest.Mock).mockResolvedValue(
        JSON.stringify({ base_url: "https://a.b" })
      );
      expect(await getMobileConfig()).toBeNull();
    });

    it("saved_at vide fallback sur ''", async () => {
      (SecureStore.getItemAsync as jest.Mock).mockResolvedValue(
        JSON.stringify({ base_url: "https://a.b", config: validConfig })
      );
      const stored = await getMobileConfig();
      expect(stored?.saved_at).toBe("");
    });

    it("retourne null si sanitize throw sur parsed.config", async () => {
      (SecureStore.getItemAsync as jest.Mock).mockResolvedValue(
        JSON.stringify({
          base_url: "https://a.b",
          config: { ...validConfig, app_name: "" },
          saved_at: "2026-01-01",
        })
      );
      expect(await getMobileConfig()).toBeNull();
    });
  });

  describe("getMobileConfigForBaseUrl", () => {
    it("retourne la config si baseUrl match (apres normalisation)", async () => {
      await saveMobileConfig("https://a.b", validConfig);
      const stored = await getMobileConfigForBaseUrl("https://a.b///");
      expect(stored?.base_url).toBe("https://a.b");
    });

    it("retourne null si baseUrl ne match pas", async () => {
      await saveMobileConfig("https://a.b", validConfig);
      expect(await getMobileConfigForBaseUrl("https://other")).toBeNull();
    });

    it("retourne null si aucune config", async () => {
      (SecureStore.getItemAsync as jest.Mock).mockResolvedValue(null);
      expect(await getMobileConfigForBaseUrl("https://a.b")).toBeNull();
    });
  });

  describe("clearMobileConfig", () => {
    it("vide le cache et supprime la cle", async () => {
      await saveMobileConfig("https://a.b", validConfig);
      await clearMobileConfig();
      expect(SecureStore.deleteItemAsync).toHaveBeenCalledWith("mf_mobile_config");
      // cache null, re-lecture passe par SecureStore
      (SecureStore.getItemAsync as jest.Mock).mockResolvedValue(null);
      expect(await getMobileConfig()).toBeNull();
    });
  });
});
