Bridge.Database.AfterMigration("sky_phone", function()

local activity_reports = {}
local allowed_blood_types = {
    [""] = true,
    ["A+"] = true,
    ["A-"] = true,
    ["B+"] = true,
    ["B-"] = true,
    ["AB+"] = true,
    ["AB-"] = true,
    ["O+"] = true,
    ["O-"] = true,
}

local function character_identifier(source, require_session)
    if require_session then
        local session, error_response = SkyPhone.RequireSession(source)
        if not session then
            return nil, error_response
        end
    end

    local identifier = Bridge.Framework.GetIdentifier(source)
    if type(identifier) ~= "string" or identifier == "" then
        return nil, { success = false, error = "health_unavailable" }
    end
    return identifier
end

local function player_name(source)
    local firstname = Bridge.Framework.GetFirstname(source)
    local lastname = Bridge.Framework.GetLastname(source)
    local name = ((firstname or "") .. " " .. (lastname or "")):match("^%s*(.-)%s*$")
    if name == "" then
        return GetPlayerName(source) or ("Player %s"):format(source)
    end
    return name
end

local function clean_text(value, maximum_length)
    if type(value) ~= "string" or value:find("[%z\1-\31]") then
        return nil
    end
    local cleaned = value:match("^%s*(.-)%s*$")
    if #cleaned > maximum_length then
        return nil
    end
    return cleaned
end

local function empty_profile(source)
    return {
        playerName = player_name(source),
        bloodType = "",
        allergies = "",
        conditions = "",
        medication = "",
        emergencyName = "",
        emergencyRelation = "",
        emergencyPhone = "",
    }
end

local function profile_for(source, identifier)
    local rows = Bridge.Database.Query([[
        SELECT `blood_type`, `allergies`, `conditions`, `medication`,
            `emergency_name`, `emergency_relation`, `emergency_phone`
        FROM `sky_phone_health_profiles`
        WHERE `owner_identifier` = ?
        LIMIT 1
    ]], { identifier })
    local row = rows[1]
    if not row then
        return empty_profile(source)
    end
    return {
        playerName = player_name(source),
        bloodType = row.blood_type or "",
        allergies = row.allergies or "",
        conditions = row.conditions or "",
        medication = row.medication or "",
        emergencyName = row.emergency_name or "",
        emergencyRelation = row.emergency_relation or "",
        emergencyPhone = row.emergency_phone or "",
    }
end

local function day_key(timestamp)
    return os.date("%Y-%m-%d", timestamp)
end

local function activity_history(identifier)
    local rows = Bridge.Database.Query([[
        SELECT DATE_FORMAT(`activity_date`, '%Y-%m-%d') AS `activity_date`,
            `steps`, `distance_meters`, `active_seconds`, `energy_kcal`
        FROM `sky_phone_health_daily`
        WHERE `owner_identifier` = ?
            AND `activity_date` >= DATE_SUB(CURDATE(), INTERVAL 13 DAY)
        ORDER BY `activity_date` ASC
    ]], { identifier })
    local by_date = {}
    for _, row in ipairs(rows) do
        by_date[row.activity_date] = row
    end

    local now = os.time()
    local days = {}
    local previous_week_steps = 0
    for days_ago = 13, 0, -1 do
        local date = day_key(now - days_ago * 86400)
        local row = by_date[date]
        local item = {
            date = date,
            steps = math.max(0, tonumber(row and row.steps) or 0),
            distanceMeters = math.max(0, tonumber(row and row.distance_meters) or 0),
            activeSeconds = math.max(0, tonumber(row and row.active_seconds) or 0),
            energyKcal = math.max(0, tonumber(row and row.energy_kcal) or 0),
        }
        if days_ago >= 7 then
            previous_week_steps = previous_week_steps + item.steps
        else
            days[#days + 1] = item
        end
    end
    return days, previous_week_steps
end

local function overview(source, identifier)
    local days, previous_week_steps = activity_history(identifier)
    return {
        dailyStepGoal = Config.Health.DailyStepGoal,
        days = days,
        previousWeekSteps = previous_week_steps,
        medicalId = profile_for(source, identifier),
        emergencyNumber = Config.Health.EmergencyNumber,
    }
end

Bridge.Callbacks.Register("sky_phone:health:overview", function(source)
    if not SkyPhone.AllowOperation(source, "health_overview", 60, 60) then
        return { success = false, error = "rate_limited" }
    end
    local identifier, error_response = character_identifier(source, true)
    if not identifier then
        return error_response
    end
    return { success = true, data = overview(source, identifier) }
end)

Bridge.Callbacks.Register("sky_phone:health:save-profile", function(source, data)
    if not SkyPhone.AllowOperation(source, "health_profile_save", 12, 60) then
        return { success = false, error = "rate_limited" }
    end
    local identifier, error_response = character_identifier(source, true)
    if not identifier then
        return error_response
    end
    if type(data) ~= "table" then
        return { success = false, error = "invalid_request" }
    end

    local blood_type = clean_text(data.bloodType, 3)
    local allergies = clean_text(data.allergies, Config.Health.ProfileTextMaxLength)
    local conditions = clean_text(data.conditions, Config.Health.ProfileTextMaxLength)
    local medication = clean_text(data.medication, Config.Health.ProfileTextMaxLength)
    local emergency_name = clean_text(data.emergencyName, 80)
    local emergency_relation = clean_text(data.emergencyRelation, 40)
    local emergency_phone = clean_text(data.emergencyPhone, 24)
    if not blood_type or not allowed_blood_types[blood_type]
        or not allergies or not conditions or not medication
        or not emergency_name or not emergency_relation or not emergency_phone
    then
        return { success = false, error = "invalid_request" }
    end
    if emergency_phone ~= "" then
        emergency_phone = SkyPhoneSimNumber.Normalize(
            emergency_phone,
            Config.Sim.NumberLength,
            Config.Sim.NumberPrefix
        )
        if not emergency_phone then
            return { success = false, error = "invalid_phone_number" }
        end
    end

    Bridge.Database.Query([[
        INSERT INTO `sky_phone_health_profiles`
            (`owner_identifier`, `blood_type`, `allergies`, `conditions`, `medication`,
                `emergency_name`, `emergency_relation`, `emergency_phone`)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        ON DUPLICATE KEY UPDATE
            `blood_type` = VALUES(`blood_type`),
            `allergies` = VALUES(`allergies`),
            `conditions` = VALUES(`conditions`),
            `medication` = VALUES(`medication`),
            `emergency_name` = VALUES(`emergency_name`),
            `emergency_relation` = VALUES(`emergency_relation`),
            `emergency_phone` = VALUES(`emergency_phone`)
    ]], {
        identifier,
        blood_type,
        allergies,
        conditions,
        medication,
        emergency_name,
        emergency_relation,
        emergency_phone,
    })
    return { success = true, data = profile_for(source, identifier) }
end)

RegisterNetEvent("sky_phone:health:record-activity", function(data)
    local player_source = source
    if type(data) ~= "table"
        or type(data.steps) ~= "number" or data.steps ~= math.floor(data.steps)
        or type(data.distanceMeters) ~= "number" or data.distanceMeters ~= math.floor(data.distanceMeters)
        or type(data.activeSeconds) ~= "number" or data.activeSeconds ~= math.floor(data.activeSeconds)
        or data.steps < 0 or data.distanceMeters < 0 or data.activeSeconds < 0
    then
        Bridge.Debug("warn", "[sky_phone] Rejected malformed health activity from source %s.", tostring(player_source))
        return
    end
    if not SkyPhone.AllowOperation(player_source, "health_activity", Config.Health.ReportsPerMinute, 60) then
        return
    end

    local identifier = character_identifier(player_source, false)
    if not identifier then
        return
    end
    local now = os.time()
    local previous = activity_reports[player_source]
    local elapsed = previous and math.max(1, now - previous) or Config.Health.ReportIntervalSeconds + 5
    activity_reports[player_source] = now
    local maximum_distance = math.ceil(elapsed * Config.Health.MaximumSpeedMetersPerSecond)
    local maximum_steps = math.ceil(data.distanceMeters / 0.35) + 4
    if data.distanceMeters > maximum_distance
        or data.activeSeconds > elapsed + 5
        or data.distanceMeters > data.activeSeconds * Config.Health.MaximumSpeedMetersPerSecond + 5
        or data.steps > maximum_steps
    then
        Bridge.Debug("warn", "[sky_phone] Rejected implausible health activity from source %s.", tostring(player_source))
        return
    end
    if data.steps == 0 and data.distanceMeters == 0 and data.activeSeconds == 0 then
        return
    end

    local energy_kcal = math.floor(data.distanceMeters * 0.06 + 0.5)
    Bridge.Database.Query([[
        INSERT INTO `sky_phone_health_daily`
            (`owner_identifier`, `activity_date`, `steps`, `distance_meters`, `active_seconds`, `energy_kcal`)
        VALUES (?, CURDATE(), ?, ?, ?, ?)
        ON DUPLICATE KEY UPDATE
            `steps` = `steps` + VALUES(`steps`),
            `distance_meters` = `distance_meters` + VALUES(`distance_meters`),
            `active_seconds` = `active_seconds` + VALUES(`active_seconds`),
            `energy_kcal` = `energy_kcal` + VALUES(`energy_kcal`)
    ]], { identifier, data.steps, data.distanceMeters, data.activeSeconds, energy_kcal })
    TriggerClientEvent("sky_phone:health:changed", player_source)
end)

AddEventHandler("playerDropped", function()
    activity_reports[source] = nil
end)

end)
