import { eq } from "drizzle-orm";
import { db } from "../db/client.js";
import { userSettings } from "../db/schema.js";
import type { Language } from "../i18n/translations.js";
import { isNtfyNotificationUrl } from "./notifications/action-renderer.js";

export type UserSettings = {
	userId: number;
	timezone?: string | null;
	emailEnabled: boolean;
	notificationEmail: string | null;
	emailStockReminders: boolean;
	emailIntakeReminders: boolean;
	emailPrescriptionReminders: boolean;
	shoutrrrEnabled: boolean;
	shoutrrrUrl: string | null;
	shoutrrrStockReminders: boolean;
	shoutrrrIntakeReminders: boolean;
	shoutrrrPrescriptionReminders: boolean;
	reminderDaysBefore: number;
	repeatDailyReminders: boolean;
	skipRemindersForTakenDoses: boolean;
	repeatRemindersEnabled: boolean;
	reminderRepeatIntervalMinutes: number;
	maxNaggingReminders: number;
	lowStockDays: number;
	normalStockDays: number;
	highStockDays: number;
	language: Language;
	stockCalculationMode: "automatic" | "manual";
	shareMedicationOverview: boolean;
	upcomingTodayOnly: boolean;
	shareScheduleTodayOnly: boolean;
	swapDashboardMainSections: boolean;
	lastAutoEmailSent: string | null;
	lastNotificationType: string | null;
	lastNotificationChannel: string | null;
	lastReminderMedName: string | null;
	lastReminderTakenBy: string | null;
	lastStockReminderSent: string | null;
	lastStockReminderChannel: string | null;
	lastStockReminderMedNames: string | null;
	lastPrescriptionReminderSent: string | null;
	lastPrescriptionReminderChannel: string | null;
	lastPrescriptionReminderMedNames: string | null;
};

export function classifyTestEmailFailure(error: unknown): { status: number; code: string; message: string } {
	const errorMessage = error instanceof Error ? error.message : "Unknown error";
	const normalizedMessage = errorMessage.toLowerCase();

	if (
		normalizedMessage.includes("smtp rejected all recipients") ||
		normalizedMessage.includes("all recipients were rejected") ||
		normalizedMessage.includes("recipient address rejected") ||
		normalizedMessage.includes("nullmx")
	) {
		return {
			status: 400,
			code: "EMAIL_RECIPIENT_REJECTED",
			message: `Failed to send email: ${errorMessage}`,
		};
	}

	if (errorMessage.includes("SMTP did not confirm accepted recipients")) {
		return {
			status: 502,
			code: "SMTP_DELIVERY_UNCONFIRMED",
			message: `Failed to send email: ${errorMessage}`,
		};
	}

	return {
		status: 500,
		code: "TEST_EMAIL_FAILED",
		message: `Failed to send email: ${errorMessage}`,
	};
}

export function getNotificationProvider(url: string): string {
	if (url.startsWith("discord://")) return "discord";
	if (url.startsWith("telegram://")) return "telegram";
	if (url.startsWith("gotify://")) return "gotify";
	if (url.startsWith("pushover://")) return "pushover";
	if (isNtfyNotificationUrl(url)) return "ntfy";

	try {
		const parsed = new URL(url);
		return parsed.hostname || "https";
	} catch {
		return "unknown";
	}
}

function envBool(key: string, defaultVal: boolean): boolean {
	const val = process.env[key];
	if (val === undefined) return defaultVal;
	return val === "true" || val === "1";
}

function envInt(key: string, defaultVal: number): number {
	const val = process.env[key];
	if (val === undefined) return defaultVal;
	const parsed = parseInt(val, 10);
	return Number.isNaN(parsed) ? defaultVal : parsed;
}

export function getDefaultSettings() {
	return {
		timezone: "",
		emailEnabled: envBool("DEFAULT_EMAIL_ENABLED", false),
		notificationEmail: process.env.DEFAULT_NOTIFICATION_EMAIL || null,
		emailStockReminders: envBool("DEFAULT_EMAIL_STOCK_REMINDERS", true),
		emailIntakeReminders: envBool("DEFAULT_EMAIL_INTAKE_REMINDERS", true),
		emailPrescriptionReminders: envBool("DEFAULT_EMAIL_PRESCRIPTION_REMINDERS", true),
		shoutrrrEnabled: envBool("DEFAULT_SHOUTRRR_ENABLED", false),
		shoutrrrUrl: process.env.DEFAULT_SHOUTRRR_URL || null,
		shoutrrrStockReminders: envBool("DEFAULT_SHOUTRRR_STOCK_REMINDERS", true),
		shoutrrrIntakeReminders: envBool("DEFAULT_SHOUTRRR_INTAKE_REMINDERS", true),
		shoutrrrPrescriptionReminders: envBool("DEFAULT_SHOUTRRR_PRESCRIPTION_REMINDERS", true),
		reminderDaysBefore: envInt("REMINDER_DAYS_BEFORE", 7),
		repeatDailyReminders: envBool("DEFAULT_REPEAT_DAILY_REMINDERS", false),
		skipRemindersForTakenDoses: envBool("DEFAULT_SKIP_REMINDERS_FOR_TAKEN_DOSES", false),
		repeatRemindersEnabled: envBool("DEFAULT_REPEAT_REMINDERS_ENABLED", false),
		reminderRepeatIntervalMinutes: envInt("DEFAULT_REMINDER_REPEAT_INTERVAL_MINUTES", 30),
		maxNaggingReminders: envInt("DEFAULT_MAX_NAGGING_REMINDERS", 5),
		lowStockDays: envInt("DEFAULT_LOW_STOCK_DAYS", 30),
		normalStockDays: envInt("DEFAULT_NORMAL_STOCK_DAYS", 90),
		highStockDays: envInt("DEFAULT_HIGH_STOCK_DAYS", 180),
		language: (process.env.DEFAULT_LANGUAGE as "en" | "de") || "en",
		stockCalculationMode: (process.env.DEFAULT_STOCK_CALCULATION_MODE as "automatic" | "manual") || "automatic",
		shareMedicationOverview: envBool("DEFAULT_SHARE_MEDICATION_OVERVIEW", false),
		upcomingTodayOnly: envBool("DEFAULT_UPCOMING_TODAY_ONLY", false),
		shareScheduleTodayOnly: envBool("DEFAULT_SHARE_SCHEDULE_TODAY_ONLY", false),
		swapDashboardMainSections: false,
		lastAutoEmailSent: null,
		lastNotificationType: null,
		lastNotificationChannel: null,
		lastReminderMedName: null,
		lastReminderTakenBy: null,
		lastStockReminderSent: null,
		lastStockReminderChannel: null,
		lastStockReminderMedNames: null,
		lastPrescriptionReminderSent: null,
		lastPrescriptionReminderChannel: null,
		lastPrescriptionReminderMedNames: null,
	};
}

type IntlWithSupportedValuesOf = typeof Intl & {
	supportedValuesOf?: (key: string) => string[];
};

let cachedTimezones: Set<string> | null = null;

function getTimezoneSet(): Set<string> {
	if (cachedTimezones) return cachedTimezones;
	const intlWithSupportedValues = Intl as IntlWithSupportedValuesOf;
	if (typeof intlWithSupportedValues.supportedValuesOf === "function") {
		cachedTimezones = new Set(intlWithSupportedValues.supportedValuesOf("timeZone"));
		return cachedTimezones;
	}
	cachedTimezones = new Set([process.env.TZ || "UTC", "UTC"]);
	return cachedTimezones;
}

export function getAvailableTimezones(): string[] {
	return [...getTimezoneSet()].sort((left, right) => left.localeCompare(right));
}

export function normalizeSettingsTimezone(value: string | null | undefined): string {
	const trimmed = value?.trim() ?? "";
	if (!trimmed) return "";
	return getTimezoneSet().has(trimmed) ? trimmed : "";
}

export function validateNotificationHostname(hostnameRaw: string): string | null {
	const hostname = hostnameRaw.toLowerCase();

	if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1") {
		return "Localhost URLs are not allowed";
	}

	const ipMatch = hostname.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
	if (ipMatch) {
		const [, a, b] = ipMatch.map(Number);
		if (
			a === 10 ||
			a === 127 ||
			(a === 172 && b >= 16 && b <= 31) ||
			(a === 192 && b === 168) ||
			(a === 169 && b === 254)
		) {
			return "Private IP addresses are not allowed";
		}
	}

	if (
		hostname.endsWith(".local") ||
		hostname.endsWith(".internal") ||
		hostname.endsWith(".lan") ||
		hostname === "metadata.google.internal"
	) {
		return "Internal hostnames are not allowed";
	}

	return null;
}

export function sanitizeNotificationUrl(
	urlStr: string
): { url: string; isNtfy: boolean; auth?: { user: string; pass: string } } | { error: string } {
	try {
		if (urlStr.startsWith("discord://")) {
			const parsedDiscord = new URL(urlStr);
			const webhookId = parsedDiscord.hostname;
			const webhookToken = parsedDiscord.username;

			if (!webhookId || !webhookToken) {
				return { error: "Invalid Discord URL format" };
			}

			if (!/^\d+$/.test(webhookId)) {
				return { error: "Invalid Discord webhook ID" };
			}

			if (!/^[A-Za-z0-9._-]+$/.test(webhookToken)) {
				return { error: "Invalid Discord webhook token" };
			}

			const discordWebhookUrl = `https://discord.com/api/webhooks/${webhookId}/${webhookToken}`;
			return { url: discordWebhookUrl, isNtfy: false };
		}

		const isNtfy = isNtfyNotificationUrl(urlStr);
		const normalizedUrl = isNtfy ? urlStr.replace("ntfy://", "https://") : urlStr;
		const parsed = new URL(normalizedUrl);

		if (!["http:", "https:"].includes(parsed.protocol)) {
			return { error: "Only HTTP/HTTPS protocols are allowed" };
		}

		const hostValidationError = validateNotificationHostname(parsed.hostname);
		if (hostValidationError) {
			return { error: hostValidationError };
		}

		const reconstructedUrl = `${parsed.protocol}//${parsed.host}${parsed.pathname}${parsed.search}`;
		const auth =
			isNtfy && parsed.username && parsed.password ? { user: parsed.username, pass: parsed.password } : undefined;

		return { url: reconstructedUrl, isNtfy, auth };
	} catch {
		return { error: "Invalid URL format" };
	}
}

async function getOrCreateUserSettings(userId: number) {
	let [settings] = await db.select().from(userSettings).where(eq(userSettings.userId, userId));

	if (!settings) {
		[settings] = await db
			.insert(userSettings)
			.values({
				userId,
				...getDefaultSettings(),
			})
			.returning();
	}

	return settings;
}

export async function loadUserSettingsFromDb(userId: number): Promise<UserSettings> {
	const settings = await getOrCreateUserSettings(userId);
	return {
		userId: settings.userId,
		timezone: settings.timezone?.trim() ? settings.timezone : null,
		emailEnabled: settings.emailEnabled,
		notificationEmail: settings.notificationEmail,
		emailStockReminders: settings.emailStockReminders,
		emailIntakeReminders: settings.emailIntakeReminders,
		emailPrescriptionReminders: settings.emailPrescriptionReminders ?? true,
		shoutrrrEnabled: settings.shoutrrrEnabled,
		shoutrrrUrl: settings.shoutrrrUrl,
		shoutrrrStockReminders: settings.shoutrrrStockReminders,
		shoutrrrIntakeReminders: settings.shoutrrrIntakeReminders,
		shoutrrrPrescriptionReminders: settings.shoutrrrPrescriptionReminders ?? true,
		reminderDaysBefore: settings.reminderDaysBefore,
		repeatDailyReminders: settings.repeatDailyReminders,
		skipRemindersForTakenDoses: settings.skipRemindersForTakenDoses ?? false,
		repeatRemindersEnabled: settings.repeatRemindersEnabled ?? false,
		reminderRepeatIntervalMinutes: settings.reminderRepeatIntervalMinutes ?? 30,
		maxNaggingReminders: settings.maxNaggingReminders ?? 5,
		lowStockDays: settings.lowStockDays,
		normalStockDays: settings.normalStockDays,
		highStockDays: settings.highStockDays,
		language: settings.language as Language,
		stockCalculationMode: (settings.stockCalculationMode as "automatic" | "manual") ?? "automatic",
		shareMedicationOverview: settings.shareMedicationOverview ?? false,
		upcomingTodayOnly: settings.upcomingTodayOnly ?? false,
		shareScheduleTodayOnly: settings.shareScheduleTodayOnly ?? false,
		swapDashboardMainSections: settings.swapDashboardMainSections ?? false,
		lastAutoEmailSent: settings.lastAutoEmailSent,
		lastNotificationType: settings.lastNotificationType,
		lastNotificationChannel: settings.lastNotificationChannel,
		lastReminderMedName: settings.lastReminderMedName ?? null,
		lastReminderTakenBy: settings.lastReminderTakenBy ?? null,
		lastStockReminderSent: settings.lastStockReminderSent ?? null,
		lastStockReminderChannel: settings.lastStockReminderChannel ?? null,
		lastStockReminderMedNames: settings.lastStockReminderMedNames ?? null,
		lastPrescriptionReminderSent: settings.lastPrescriptionReminderSent ?? null,
		lastPrescriptionReminderChannel: settings.lastPrescriptionReminderChannel ?? null,
		lastPrescriptionReminderMedNames: settings.lastPrescriptionReminderMedNames ?? null,
	};
}

export async function getAllUserSettingsFromDb(): Promise<UserSettings[]> {
	const allSettings = await db.select().from(userSettings);
	return allSettings.map((settings) => ({
		userId: settings.userId,
		timezone: settings.timezone?.trim() ? settings.timezone : null,
		emailEnabled: settings.emailEnabled,
		notificationEmail: settings.notificationEmail,
		emailStockReminders: settings.emailStockReminders,
		emailIntakeReminders: settings.emailIntakeReminders,
		emailPrescriptionReminders: settings.emailPrescriptionReminders ?? true,
		shoutrrrEnabled: settings.shoutrrrEnabled,
		shoutrrrUrl: settings.shoutrrrUrl,
		shoutrrrStockReminders: settings.shoutrrrStockReminders,
		shoutrrrIntakeReminders: settings.shoutrrrIntakeReminders,
		shoutrrrPrescriptionReminders: settings.shoutrrrPrescriptionReminders ?? true,
		reminderDaysBefore: settings.reminderDaysBefore,
		repeatDailyReminders: settings.repeatDailyReminders,
		skipRemindersForTakenDoses: settings.skipRemindersForTakenDoses ?? false,
		repeatRemindersEnabled: settings.repeatRemindersEnabled ?? false,
		reminderRepeatIntervalMinutes: settings.reminderRepeatIntervalMinutes ?? 30,
		maxNaggingReminders: settings.maxNaggingReminders ?? 5,
		lowStockDays: settings.lowStockDays,
		normalStockDays: settings.normalStockDays,
		highStockDays: settings.highStockDays,
		language: settings.language as Language,
		stockCalculationMode: (settings.stockCalculationMode as "automatic" | "manual") ?? "automatic",
		shareMedicationOverview: settings.shareMedicationOverview ?? false,
		upcomingTodayOnly: settings.upcomingTodayOnly ?? false,
		shareScheduleTodayOnly: settings.shareScheduleTodayOnly ?? false,
		swapDashboardMainSections: settings.swapDashboardMainSections ?? false,
		lastAutoEmailSent: settings.lastAutoEmailSent,
		lastNotificationType: settings.lastNotificationType,
		lastNotificationChannel: settings.lastNotificationChannel,
		lastReminderMedName: settings.lastReminderMedName ?? null,
		lastReminderTakenBy: settings.lastReminderTakenBy ?? null,
		lastStockReminderSent: settings.lastStockReminderSent ?? null,
		lastStockReminderChannel: settings.lastStockReminderChannel ?? null,
		lastStockReminderMedNames: settings.lastStockReminderMedNames ?? null,
		lastPrescriptionReminderSent: settings.lastPrescriptionReminderSent ?? null,
		lastPrescriptionReminderChannel: settings.lastPrescriptionReminderChannel ?? null,
		lastPrescriptionReminderMedNames: settings.lastPrescriptionReminderMedNames ?? null,
	}));
}
