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

const mockSelectFromWhereOrderBy = vi.fn();

vi.mock("~/server/db", () => ({
  db: {
    select: vi.fn(() => ({
      from: vi.fn(() => ({
        where: vi.fn(() => ({
          orderBy: mockSelectFromWhereOrderBy,
          limit: vi.fn().mockResolvedValue([]),
        })),
        orderBy: vi.fn(() => ({
          limit: vi.fn(() => ({
            offset: vi.fn().mockResolvedValue([]),
          })),
        })),
      })),
    })),
    insert: vi.fn(() => ({
      values: vi.fn(() => ({
        returning: vi.fn().mockResolvedValue([{ id: "new-id" }]),
      })),
    })),
    update: vi.fn(() => ({
      set: vi.fn(() => ({
        where: vi.fn(() => ({
          returning: vi.fn().mockResolvedValue([{ id: "updated-id" }]),
        })),
      })),
    })),
  },
}));

vi.mock("~/server/db/schema", () => ({
  spamFeedback: {},
  spamRules: {},
  auditLogs: {},
}));

vi.mock("./spamshield/ml.engine", () => ({
  classifyTextBERT: vi.fn(),
  extractFeatures: vi.fn(),
  ruleEngine: vi.fn(),
}));

vi.mock("./spamshield/reputation.api", () => ({
  checkReputation: vi.fn(),
  lookupInternalDB: vi.fn(),
  lookupHiya: vi.fn(),
  lookupTruecaller: vi.fn(),
  cacheReputation: vi.fn(),
}));

import * as spamshieldService from "./spamshield.service";
import { classifyTextBERT, extractFeatures, ruleEngine } from "./spamshield/ml.engine";
import { checkReputation } from "./spamshield/reputation.api";

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

describe("spamshield.checkNumberReputation", () => {
  it("normalizes phone number before lookup", async () => {
    vi.mocked(checkReputation).mockResolvedValue(null);
    const result = await spamshieldService.checkNumberReputation("234567890");
    expect(result.phoneNumber).toBe("+1234567890");
  });

  it("returns reputation when found", async () => {
    vi.mocked(checkReputation).mockResolvedValue({
      source: "hiya",
      score: 0.9,
      isSpam: true,
      confidence: 0.85,
      category: "spam",
    });
    const result = await spamshieldService.checkNumberReputation("+1234567890");
    expect(result.isSpam).toBe(true);
    expect(result.source).toBe("hiya");
  });

  it("returns default when reputation not found", async () => {
    vi.mocked(checkReputation).mockResolvedValue(null);
    const result = await spamshieldService.checkNumberReputation("+1234567890");
    expect(result.isSpam).toBe(false);
    expect(result.source).toBe("internal");
  });
});

describe("spamshield.classifySMS", () => {
  it("classifies text as ham", async () => {
    vi.mocked(classifyTextBERT).mockResolvedValue({ isSpam: false, confidence: 1.0, score: 0.0 });
    const result = await spamshieldService.classifySMS("Hello friend");
    expect(result.isSpam).toBe(false);
  });

  it("classifies text as spam", async () => {
    vi.mocked(classifyTextBERT).mockResolvedValue({ isSpam: true, confidence: 0.95, score: 0.95 });
    const result = await spamshieldService.classifySMS("Win free money now");
    expect(result.isSpam).toBe(true);
    expect(result.confidence).toBe(0.95);
  });
});

describe("spamshield.classifyCall", () => {
  beforeEach(() => {
    vi.mocked(extractFeatures).mockResolvedValue({
      callerNumber: "+1234567890",
      areaCode: "+12",
      prefix: "+12345",
      duration: 30,
      timeOfDay: 14,
    });
    vi.mocked(ruleEngine).mockResolvedValue(null);
    vi.mocked(checkReputation).mockResolvedValue(null);
    mockSelectFromWhereOrderBy.mockResolvedValue([]);
  });

  it("analyzes call metadata", async () => {
    const result = await spamshieldService.classifyCall("+1234567890", 30, 14);
    expect(result.callerNumber).toBe("+1234567890");
  });

  it("flags blocked numbers", async () => {
    vi.mocked(ruleEngine).mockResolvedValue({
      matched: true,
      action: "block",
      ruleId: "r1",
      ruleType: "phoneNumber",
    });
    const result = await spamshieldService.classifyCall("+1234567890");
    expect(result.isSpam).toBe(true);
    expect(result.matchedRule?.action).toBe("block");
  });
});

describe("spamshield.createRule", () => {
  it("creates rule in database", async () => {
    const fakeRule = { id: "r1", userId: "user-1", ruleType: "prefix", pattern: "+123", action: "block", priority: 0, isActive: true, isGlobal: false, createdAt: new Date(), updatedAt: new Date() };

    const mockDb = await import("~/server/db");
    vi.mocked(mockDb.db.insert).mockReturnValue({
      values: vi.fn().mockReturnValue({ returning: vi.fn().mockResolvedValue([fakeRule]) }),
    } as never);

    const result = await spamshieldService.createRule("user-1", "prefix", "+123", "block", 0);
    expect(result.id).toBe("r1");
    expect(result.pattern).toBe("+123");
  });
});
