const fs = require("node:fs");
const path = require("node:path");
const { dataDir, defaultUser, defaultSystemConfig } = require("./config.cjs");
const { addYears, nowIso, passwordHash } = require("./security.cjs");

const dbPath = path.join(dataDir, "users.json");
let memoryDb = null;

function createInitialDb() {
    const expiresAt = addYears(new Date(), 100).toISOString();
    const user = {
        id: 1,
        username: defaultUser.username,
        email: defaultUser.email,
        password: passwordHash(defaultUser.password),
        role: "admin",
        status: "active",
        points: 999999,
        is_super_admin: 1,
        owner_admin_id: null,
        invite_code: "LOCALADMIN",
        max_devices: 99,
        device_fingerprint: "",
        created_at: nowIso(),
        updated_at: nowIso(),
        last_login_at: null,
        last_login_device: null,
        current_token: "",
        cloud_digital_human_disabled: 0,
    };

    return {
        nextUserId: 2,
        nextSubscriptionId: 2,
        nextUserLogId: 1,
        nextGenerationLogId: 1,
        users: [user],
        subscriptions: [
            {
                id: 1,
                user_id: 1,
                subscription_type: "local",
                expires_at: expiresAt,
                trial_started_at: nowIso(),
                created_at: nowIso(),
                updated_at: nowIso(),
            },
        ],
        generationLogs: [],
        userLogs: [],
        devices: [],
        systemConfigs: { ...defaultSystemConfig },
    };
}

function normalizeSystemConfigs(value) {
    if (Array.isArray(value)) {
        return value.reduce((configs, item) => {
            if (item?.config_key) configs[item.config_key] = String(item.config_value ?? "");
            return configs;
        }, { ...defaultSystemConfig });
    }
    return { ...defaultSystemConfig, ...(value || {}) };
}

function normalizeDb(db) {
    const initial = createInitialDb();
    const normalized = { ...initial, ...(db || {}) };
    normalized.users = Array.isArray(normalized.users) ? normalized.users : initial.users;
    normalized.subscriptions = Array.isArray(normalized.subscriptions) ? normalized.subscriptions : initial.subscriptions;
    normalized.generationLogs = Array.isArray(normalized.generationLogs) ? normalized.generationLogs : [];
    normalized.userLogs = Array.isArray(normalized.userLogs) ? normalized.userLogs : [];
    normalized.devices = Array.isArray(normalized.devices) ? normalized.devices : [];
    normalized.systemConfigs = normalizeSystemConfigs(normalized.systemConfigs);

    for (const user of normalized.users) {
        user.role = user.role || "user";
        user.status = user.status || "active";
        user.points = Number(user.points || 0);
        user.max_devices = Number(user.max_devices || 1);
        user.is_super_admin = Number(user.is_super_admin || 0);
        user.cloud_digital_human_disabled = Number(user.cloud_digital_human_disabled || 0);
        user.created_at = user.created_at || nowIso();
        user.updated_at = user.updated_at || nowIso();
    }

    normalized.nextUserId = Math.max(
        Number(normalized.nextUserId || 1),
        ...normalized.users.map(user => Number(user.id || 0) + 1),
    );
    normalized.nextSubscriptionId = Math.max(
        Number(normalized.nextSubscriptionId || 1),
        ...normalized.subscriptions.map(item => Number(item.id || 0) + 1),
    );
    normalized.nextUserLogId = Math.max(
        Number(normalized.nextUserLogId || 1),
        ...normalized.userLogs.map(item => Number(item.id || 0) + 1),
    );
    normalized.nextGenerationLogId = Math.max(
        Number(normalized.nextGenerationLogId || 1),
        ...normalized.generationLogs.map(item => Number(item.id || 0) + 1),
    );

    return normalized;
}

function ensureDb() {
    if (memoryDb) return;

    try {
        fs.mkdirSync(dataDir, { recursive: true });

        if (!fs.existsSync(dbPath)) {
            fs.writeFileSync(dbPath, JSON.stringify(createInitialDb(), null, 2), "utf8");
        }
    } catch (error) {
        memoryDb = createInitialDb();
        console.warn(`[local-auth] cannot write ${dbPath}; using in-memory database: ${error.message}`);
    }
}

function loadDb() {
    ensureDb();
    if (memoryDb) return memoryDb;
    return normalizeDb(JSON.parse(fs.readFileSync(dbPath, "utf8")));
}

function saveDb(db) {
    const normalized = normalizeDb(db);
    if (memoryDb) {
        memoryDb = normalized;
        return;
    }
    fs.writeFileSync(dbPath, JSON.stringify(normalized, null, 2), "utf8");
}

function getSubscription(db, userId) {
    const existing = db.subscriptions.find(item => Number(item.user_id) === Number(userId));
    if (existing) return existing;

    const subscription = {
        id: db.nextSubscriptionId++,
        user_id: Number(userId),
        subscription_type: "local",
        expires_at: addYears(new Date(), 100).toISOString(),
        trial_started_at: nowIso(),
        created_at: nowIso(),
        updated_at: nowIso(),
    };
    db.subscriptions.push(subscription);
    return subscription;
}

function addUserLog(db, userId, action, message, extra = {}) {
    db.userLogs.push({
        id: db.nextUserLogId++,
        user_id: Number(userId || 0) || null,
        action,
        message,
        device_fingerprint: extra.device_fingerprint || null,
        ip_address: extra.ip_address || "127.0.0.1",
        created_at: nowIso(),
    });
}

function addGenerationLog(db, userId, type, detail, extra = {}) {
    db.generationLogs.push({
        id: db.nextGenerationLogId++,
        user_id: Number(userId || 0) || null,
        type: type || "unknown",
        detail: typeof detail === "string" ? detail : JSON.stringify(detail || {}),
        device_fingerprint: extra.device_fingerprint || null,
        ip_address: extra.ip_address || "127.0.0.1",
        created_at: nowIso(),
    });
}

module.exports = {
    dbPath,
    ensureDb,
    loadDb,
    saveDb,
    getSubscription,
    addUserLog,
    addGenerationLog,
};
