/**
 * Tests for /settings API endpoints.
 * Tests user settings CRUD operations.
 */
import { describe, it, expect, beforeAll, afterAll, beforeEach } from "vitest";
import {
  buildTestApp,
  closeTestApp,
  clearTestData,
  createTestUser,
  setUserSettings,
  TestContext,
} from "./setup.js";

// =============================================================================
// Route Registration
// =============================================================================

async function registerSettingsRoutes(ctx: TestContext) {
  const { app, client } = ctx;

  // GET /settings - Get user settings
  app.get("/settings", async (request, reply) => {
    const userId = 1;

    const result = await client.execute({
      sql: `SELECT * FROM user_settings WHERE user_id = ?`,
      args: [userId],
    });

    if (result.rows.length === 0) {
      // Return defaults
      return {
        emailEnabled: false,
        notificationEmail: "",
        emailStockReminders: true,
        emailIntakeReminders: true,
        shoutrrrEnabled: false,
        shoutrrrUrl: "",
        shoutrrrStockReminders: true,
        shoutrrrIntakeReminders: true,
        reminderDaysBefore: 7,
        repeatDailyReminders: false,
        skipRemindersForTakenDoses: false,
        repeatRemindersEnabled: false,
        reminderRepeatIntervalMinutes: 30,
        maxNaggingReminders: 5,
        lowStockDays: 30,
        normalStockDays: 90,
        highStockDays: 180,
        expiryWarningDays: 90,
        language: "en",
        stockCalculationMode: "automatic",
      };
    }

    const s = result.rows[0];
    return {
      emailEnabled: Boolean(s.email_enabled),
      notificationEmail: s.notification_email || "",
      emailStockReminders: Boolean(s.email_stock_reminders),
      emailIntakeReminders: Boolean(s.email_intake_reminders),
      shoutrrrEnabled: Boolean(s.shoutrrr_enabled),
      shoutrrrUrl: s.shoutrrr_url || "",
      shoutrrrStockReminders: Boolean(s.shoutrrr_stock_reminders),
      shoutrrrIntakeReminders: Boolean(s.shoutrrr_intake_reminders),
      reminderDaysBefore: s.reminder_days_before,
      repeatDailyReminders: Boolean(s.repeat_daily_reminders),
      skipRemindersForTakenDoses: Boolean(s.skip_reminders_for_taken_doses ?? false),
      repeatRemindersEnabled: Boolean(s.repeat_reminders_enabled ?? false),
      reminderRepeatIntervalMinutes: s.reminder_repeat_interval_minutes ?? 30,
      maxNaggingReminders: s.max_nagging_reminders ?? 5,
      lowStockDays: s.low_stock_days,
      normalStockDays: s.normal_stock_days,
      highStockDays: s.high_stock_days,
      expiryWarningDays: s.expiry_warning_days,
      language: s.language,
      stockCalculationMode: s.stock_calculation_mode,
    };
  });

  // PUT /settings - Update user settings
  app.put<{
    Body: {
      emailEnabled?: boolean;
      notificationEmail?: string;
      emailStockReminders?: boolean;
      emailIntakeReminders?: boolean;
      shoutrrrEnabled?: boolean;
      shoutrrrUrl?: string;
      shoutrrrStockReminders?: boolean;
      shoutrrrIntakeReminders?: boolean;
      reminderDaysBefore?: number;
      repeatDailyReminders?: boolean;
      skipRemindersForTakenDoses?: boolean;
      repeatRemindersEnabled?: boolean;
      reminderRepeatIntervalMinutes?: number;
      maxNaggingReminders?: number;
      lowStockDays?: number;
      normalStockDays?: number;
      highStockDays?: number;
      expiryWarningDays?: number;
      language?: string;
      stockCalculationMode?: "automatic" | "manual";
    };
  }>("/settings", async (request, reply) => {
    const userId = 1;
    const body = request.body || {};

    // Validation
    if (body.emailEnabled && !body.notificationEmail) {
      return reply.status(400).send({ error: "Email address required when email is enabled" });
    }
    if (body.notificationEmail && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(body.notificationEmail)) {
      return reply.status(400).send({ error: "Invalid email address" });
    }
    if (body.lowStockDays !== undefined && (body.lowStockDays < 1 || body.lowStockDays > 365)) {
      return reply.status(400).send({ error: "lowStockDays must be between 1 and 365" });
    }
    if (body.language && !["en", "de"].includes(body.language)) {
      return reply.status(400).send({ error: "Language must be 'en' or 'de'" });
    }
    if (body.stockCalculationMode && !["automatic", "manual"].includes(body.stockCalculationMode)) {
      return reply.status(400).send({ error: "stockCalculationMode must be 'automatic' or 'manual'" });
    }
    if (body.reminderRepeatIntervalMinutes !== undefined && (body.reminderRepeatIntervalMinutes < 5 || body.reminderRepeatIntervalMinutes > 480)) {
      return reply.status(400).send({ error: "reminderRepeatIntervalMinutes must be between 5 and 480" });
    }
    if (body.maxNaggingReminders !== undefined && (body.maxNaggingReminders < 1 || body.maxNaggingReminders > 20)) {
      return reply.status(400).send({ error: "maxNaggingReminders must be between 1 and 20" });
    }

    // Check if settings exist
    const existing = await client.execute({
      sql: `SELECT id FROM user_settings WHERE user_id = ?`,
      args: [userId],
    });

    if (existing.rows.length === 0) {
      // Insert new settings
      await client.execute({
        sql: `INSERT INTO user_settings (
          user_id, email_enabled, notification_email,
          email_stock_reminders, email_intake_reminders,
          shoutrrr_enabled, shoutrrr_url,
          shoutrrr_stock_reminders, shoutrrr_intake_reminders,
          reminder_days_before, repeat_daily_reminders, skip_reminders_for_taken_doses,
          repeat_reminders_enabled, reminder_repeat_interval_minutes, max_nagging_reminders,
          low_stock_days, normal_stock_days, high_stock_days,
          expiry_warning_days, language, stock_calculation_mode
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        args: [
          userId,
          body.emailEnabled ? 1 : 0,
          body.notificationEmail || null,
          body.emailStockReminders !== false ? 1 : 0,
          body.emailIntakeReminders !== false ? 1 : 0,
          body.shoutrrrEnabled ? 1 : 0,
          body.shoutrrrUrl || null,
          body.shoutrrrStockReminders !== false ? 1 : 0,
          body.shoutrrrIntakeReminders !== false ? 1 : 0,
          body.reminderDaysBefore ?? 7,
          body.repeatDailyReminders ? 1 : 0,
          body.skipRemindersForTakenDoses ? 1 : 0,
          body.repeatRemindersEnabled ? 1 : 0,
          body.reminderRepeatIntervalMinutes ?? 30,
          body.maxNaggingReminders ?? 5,
          body.lowStockDays ?? 30,
          body.normalStockDays ?? 90,
          body.highStockDays ?? 180,
          body.expiryWarningDays ?? 90,
          body.language || "en",
          body.stockCalculationMode || "automatic",
        ],
      });
    } else {
      // Update existing settings
      await client.execute({
        sql: `UPDATE user_settings SET
          email_enabled = ?,
          notification_email = ?,
          email_stock_reminders = ?,
          email_intake_reminders = ?,
          shoutrrr_enabled = ?,
          shoutrrr_url = ?,
          shoutrrr_stock_reminders = ?,
          shoutrrr_intake_reminders = ?,
          reminder_days_before = ?,
          repeat_daily_reminders = ?,
          skip_reminders_for_taken_doses = ?,
          repeat_reminders_enabled = ?,
          reminder_repeat_interval_minutes = ?,
          max_nagging_reminders = ?,
          low_stock_days = ?,
          normal_stock_days = ?,
          high_stock_days = ?,
          expiry_warning_days = ?,
          language = ?,
          stock_calculation_mode = ?,
          updated_at = strftime('%s','now')
        WHERE user_id = ?`,
        args: [
          body.emailEnabled ? 1 : 0,
          body.notificationEmail || null,
          body.emailStockReminders !== false ? 1 : 0,
          body.emailIntakeReminders !== false ? 1 : 0,
          body.shoutrrrEnabled ? 1 : 0,
          body.shoutrrrUrl || null,
          body.shoutrrrStockReminders !== false ? 1 : 0,
          body.shoutrrrIntakeReminders !== false ? 1 : 0,
          body.reminderDaysBefore ?? 7,
          body.repeatDailyReminders ? 1 : 0,
          body.skipRemindersForTakenDoses ? 1 : 0,
          body.repeatRemindersEnabled ? 1 : 0,
          body.reminderRepeatIntervalMinutes ?? 30,
          body.maxNaggingReminders ?? 5,
          body.lowStockDays ?? 30,
          body.normalStockDays ?? 90,
          body.highStockDays ?? 180,
          body.expiryWarningDays ?? 90,
          body.language || "en",
          body.stockCalculationMode || "automatic",
          userId,
        ],
      });
    }

    return { success: true };
  });
}

// =============================================================================
// Tests
// =============================================================================

describe("Settings API", () => {
  let ctx: TestContext;
  let userId: number;

  beforeAll(async () => {
    ctx = await buildTestApp();
    await registerSettingsRoutes(ctx);
    await ctx.app.ready();
  });

  afterAll(async () => {
    await closeTestApp(ctx);
  });

  beforeEach(async () => {
    await clearTestData(ctx.client);
    await ctx.client.execute("DELETE FROM sqlite_sequence WHERE name='users'");
    userId = await createTestUser(ctx.client, { username: "testuser" });
  });

  // ---------------------------------------------------------------------------
  // GET /settings
  // ---------------------------------------------------------------------------

  describe("GET /settings", () => {
    it("should return default settings for new user", async () => {
      const response = await ctx.app.inject({
        method: "GET",
        url: "/settings",
      });

      expect(response.statusCode).toBe(200);
      const data = response.json();
      expect(data.emailEnabled).toBe(false);
      expect(data.lowStockDays).toBe(30);
      expect(data.normalStockDays).toBe(90);
      expect(data.highStockDays).toBe(180);
      expect(data.language).toBe("en");
      expect(data.stockCalculationMode).toBe("automatic");
    });

    it("should return saved settings", async () => {
      // Create settings first
      await setUserSettings(ctx.client, {
        userId,
        stockCalculationMode: "manual",
        lowStockDays: 14,
      });

      const response = await ctx.app.inject({
        method: "GET",
        url: "/settings",
      });

      expect(response.statusCode).toBe(200);
      const data = response.json();
      expect(data.stockCalculationMode).toBe("manual");
      expect(data.lowStockDays).toBe(14);
    });
  });

  // ---------------------------------------------------------------------------
  // PUT /settings
  // ---------------------------------------------------------------------------

  describe("PUT /settings", () => {
    it("should create settings for new user", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          language: "de",
          lowStockDays: 14,
          stockCalculationMode: "manual",
        },
      });

      expect(response.statusCode).toBe(200);
      expect(response.json()).toEqual({ success: true });

      // Verify
      const result = await ctx.client.execute({
        sql: `SELECT language, low_stock_days, stock_calculation_mode FROM user_settings WHERE user_id = ?`,
        args: [userId],
      });
      expect(result.rows[0].language).toBe("de");
      expect(result.rows[0].low_stock_days).toBe(14);
      expect(result.rows[0].stock_calculation_mode).toBe("manual");
    });

    it("should update existing settings", async () => {
      // Create initial settings
      await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: { language: "en" },
      });

      // Update
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: { language: "de" },
      });

      expect(response.statusCode).toBe(200);

      // Verify
      const result = await ctx.client.execute({
        sql: `SELECT language FROM user_settings WHERE user_id = ?`,
        args: [userId],
      });
      expect(result.rows[0].language).toBe("de");
    });

    it("should enable email notifications", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          emailEnabled: true,
          notificationEmail: "test@example.com",
          emailStockReminders: true,
          emailIntakeReminders: false,
        },
      });

      expect(response.statusCode).toBe(200);

      // Verify
      const result = await ctx.client.execute({
        sql: `SELECT email_enabled, notification_email, email_stock_reminders, email_intake_reminders 
              FROM user_settings WHERE user_id = ?`,
        args: [userId],
      });
      expect(result.rows[0].email_enabled).toBe(1);
      expect(result.rows[0].notification_email).toBe("test@example.com");
      expect(result.rows[0].email_stock_reminders).toBe(1);
      expect(result.rows[0].email_intake_reminders).toBe(0);
    });

    it("should reject email enabled without email address", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          emailEnabled: true,
        },
      });

      expect(response.statusCode).toBe(400);
      expect(response.json().error).toBe("Email address required when email is enabled");
    });

    it("should reject invalid email address", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          notificationEmail: "not-an-email",
        },
      });

      expect(response.statusCode).toBe(400);
      expect(response.json().error).toBe("Invalid email address");
    });

    it("should reject invalid lowStockDays", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          lowStockDays: 0,
        },
      });

      expect(response.statusCode).toBe(400);
      expect(response.json().error).toBe("lowStockDays must be between 1 and 365");
    });

    it("should reject invalid language", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          language: "fr",
        },
      });

      expect(response.statusCode).toBe(400);
      expect(response.json().error).toBe("Language must be 'en' or 'de'");
    });

    it("should reject invalid stockCalculationMode", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          stockCalculationMode: "invalid",
        },
      });

      expect(response.statusCode).toBe(400);
      expect(response.json().error).toBe("stockCalculationMode must be 'automatic' or 'manual'");
    });

    it("should enable shoutrrr notifications", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          shoutrrrEnabled: true,
          shoutrrrUrl: "ntfy://ntfy.sh/mytopic",
          shoutrrrStockReminders: true,
          shoutrrrIntakeReminders: true,
        },
      });

      expect(response.statusCode).toBe(200);

      // Verify
      const result = await ctx.client.execute({
        sql: `SELECT shoutrrr_enabled, shoutrrr_url FROM user_settings WHERE user_id = ?`,
        args: [userId],
      });
      expect(result.rows[0].shoutrrr_enabled).toBe(1);
      expect(result.rows[0].shoutrrr_url).toBe("ntfy://ntfy.sh/mytopic");
    });

    it("should update threshold settings", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          lowStockDays: 14,
          normalStockDays: 60,
          highStockDays: 120,
          expiryWarningDays: 30,
        },
      });

      expect(response.statusCode).toBe(200);

      // Verify
      const result = await ctx.client.execute({
        sql: `SELECT low_stock_days, normal_stock_days, high_stock_days, expiry_warning_days 
              FROM user_settings WHERE user_id = ?`,
        args: [userId],
      });
      expect(result.rows[0].low_stock_days).toBe(14);
      expect(result.rows[0].normal_stock_days).toBe(60);
      expect(result.rows[0].high_stock_days).toBe(120);
      expect(result.rows[0].expiry_warning_days).toBe(30);
    });
  });

  // ---------------------------------------------------------------------------
  // Stock Calculation Mode
  // ---------------------------------------------------------------------------

  describe("Stock Calculation Mode", () => {
    it("should switch to manual mode", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          stockCalculationMode: "manual",
        },
      });

      expect(response.statusCode).toBe(200);

      const getResponse = await ctx.app.inject({
        method: "GET",
        url: "/settings",
      });

      expect(getResponse.json().stockCalculationMode).toBe("manual");
    });

    it("should switch back to automatic mode", async () => {
      // Set to manual first
      await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: { stockCalculationMode: "manual" },
      });

      // Switch back
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: { stockCalculationMode: "automatic" },
      });

      expect(response.statusCode).toBe(200);

      const getResponse = await ctx.app.inject({
        method: "GET",
        url: "/settings",
      });

      expect(getResponse.json().stockCalculationMode).toBe("automatic");
    });
  });

  // ---------------------------------------------------------------------------
  // Repeat Reminders & Skip Reminders Settings
  // ---------------------------------------------------------------------------

  describe("Repeat Reminders Settings", () => {
    it("should enable repeat reminders with interval", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          repeatRemindersEnabled: true,
          reminderRepeatIntervalMinutes: 10,
        },
      });

      expect(response.statusCode).toBe(200);

      const getResponse = await ctx.app.inject({
        method: "GET",
        url: "/settings",
      });

      const settings = getResponse.json();
      expect(settings.repeatRemindersEnabled).toBe(true);
      expect(settings.reminderRepeatIntervalMinutes).toBe(10);
    });

    it("should validate repeat interval range", async () => {
      let response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          repeatRemindersEnabled: true,
          reminderRepeatIntervalMinutes: 2,
        },
      });
      expect(response.statusCode).toBe(400);

      response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          repeatRemindersEnabled: true,
          reminderRepeatIntervalMinutes: 500,
        },
      });
      expect(response.statusCode).toBe(400);
    });

    it("should validate max nagging reminders range", async () => {
      let response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          maxNaggingReminders: 0,
        },
      });
      expect(response.statusCode).toBe(400);

      response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          maxNaggingReminders: 25,
        },
      });
      expect(response.statusCode).toBe(400);

      // Valid values should work
      response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          maxNaggingReminders: 10,
        },
      });
      expect(response.statusCode).toBe(200);

      const getResponse = await ctx.app.inject({
        method: "GET",
        url: "/settings",
      });

      const settings = getResponse.json();
      expect(settings.maxNaggingReminders).toBe(10);
    });
  });

  describe("Skip Reminders for Taken Doses", () => {
    it("should enable and disable skip reminders setting", async () => {
      let response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          skipRemindersForTakenDoses: true,
        },
      });
      expect(response.statusCode).toBe(200);

      response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          skipRemindersForTakenDoses: false,
        },
      });
      expect(response.statusCode).toBe(200);
    });

    it("should work with repeat reminders enabled", async () => {
      const response = await ctx.app.inject({
        method: "PUT",
        url: "/settings",
        payload: {
          repeatRemindersEnabled: true,
          reminderRepeatIntervalMinutes: 5,
          skipRemindersForTakenDoses: true,
        },
      });
      expect(response.statusCode).toBe(200);

      const getResponse = await ctx.app.inject({
        method: "GET",
        url: "/settings",
      });

      const settings = getResponse.json();
      expect(settings.repeatRemindersEnabled).toBe(true);
      expect(settings.reminderRepeatIntervalMinutes).toBe(5);
      expect(settings.skipRemindersForTakenDoses).toBe(true);
    });
  });
});
