#!/bin/bash
# Deepin System Power Control - Dynamic TLP Mode Manager
# Processes configuration files and applies mode-specific settings.

set -euo pipefail

# ==============================================================================
# Constants
# ==============================================================================

readonly TLP_DIR="/usr/share/tlp/deepin-system-power-control"
readonly TLP_CONFIG="/etc/tlp.d"
readonly SCRIPT_NAME="deepin-system-power-control"

readonly VALID_MODES=("performance" "saving" "balance" "lowbat")
readonly VALID_COMMANDS=("set" "init-start")

# ==============================================================================
# Global Variables
# ==============================================================================

DEBUG=false  # Default: don't show debug logs

# ==============================================================================
# Logging Functions
# ==============================================================================

# Get current timestamp: YYYY-MM-DD HH:MM:SS
_timestamp() {
    date '+%Y-%m-%d %H:%M:%S'
}

# Log debug message (only if DEBUG is true)
log_debug() {
    if [[ "$DEBUG" == "true" ]]; then
        printf '%s [%s] %s\n' "$(_timestamp)" "DEBUG" "$*" >&2
    fi
}

# Log error message (always shown)
log_error() {
    printf '%s [%s] %s\n' "$(_timestamp)" "ERROR" "$*" >&2
}

# Log info message (always shown)
log_info() {
    printf '%s [%s] %s\n' "$(_timestamp)" "INFO" "$*" >&2
}

# ==============================================================================
# Utility Functions (Updated)
# ==============================================================================

usage() {
    cat <<EOF
Usage: $0 [-d] <command> [options]

Options:
  -d                Enable debug logging

Commands:
  set <mode>        Set power mode
    Modes: ${VALID_MODES[*]}

  init-start        Start TLP service without changing config

Examples:
  $0 set performance
  $0 -d set performance    # with debug output
  $0 init-start
EOF
}

check_root() {
    if [[ "$(id -u)" != "0" ]]; then
        log_error "This script requires root privileges"
        exit 1
    fi
}

validate_command() {
    local cmd="$1"
    if ! printf '%s\n' "${VALID_COMMANDS[@]}" | grep -qx "$cmd"; then
        log_error "Invalid command. Supported: ${VALID_COMMANDS[*]}"
        usage
        exit 1
    fi
}

validate_mode() {
    local mode="$1"
    if ! printf '%s\n' "${VALID_MODES[@]}" | grep -qx "$mode"; then
        log_error "Invalid mode. Supported: ${VALID_MODES[*]}"
        usage
        exit 1
    fi
}

# Checks if CPU supports EPP (Energy Performance Preference)
check_epp() {
    local INTEL_PSTATED="/sys/devices/system/cpu/intel_pstate"
    local AMD_PSTATED="/sys/devices/system/cpu/amd_pstate"
    local CPU_INFO="/proc/cpuinfo"

    if [[ -f "/sys/devices/system/cpu/cpu0/cpufreq/energy_performance_available_preferences" ]]; then
        return 0
    fi

    if [[ -d "$INTEL_PSTATED" ]]; then
        if grep -q -m1 'hwp_epp' "$CPU_INFO" 2>/dev/null; then
            return 0
        fi
    fi

    if [[ -d "$AMD_PSTATED" ]]; then
        if [[ "$(cat "$AMD_PSTATED/status" 2>/dev/null)" == "active" ]]; then
            return 0
        fi
    fi

    return 1
}

# Extracts configuration under a specific section from an INI-style file
extract_section() {
    local file="$1"
    local section="$2"
    local in_section=0
    local in_header=1

    while IFS= read -r line; do
        # If currently in header (before any section), check if this line is a section header
        if [[ $in_header -eq 1 ]] && [[ "$line" =~ ^\[.*\][[:space:]]*(#.*)?$ ]]; then
            in_header=0
        fi

        # Output the line if still in header (e.g. comments, blank lines, etc.)
        if [ $in_header -eq 1 ]; then
            echo "$line"
            continue
        fi

        # Skip empty lines and comments (including indented ones)
        [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue

        # Match section header with optional trailing whitespace/comments
        if [[ "$line" =~ ^\[${section}\][[:space:]]*(#.*)?$ ]]; then
            in_section=1
            log_debug "Entered section [$section]"
            continue
        fi

        # If already in section and encounter a new section header, exit
        if [[ "$line" =~ ^\[.*\][[:space:]]*(#.*)?$ ]] && [[ $in_section -eq 1 ]]; then
            log_debug "Exited section (new section: $line)"
            break
        fi

        # Output key=value lines inside the section
        if [[ $in_section -eq 1 && "$line" == *"="* ]]; then
            echo "$line"
        fi
    done < "$file"
}

# Removes old configuration files generated by this script
cleanup_old_configs() {
    log_info "Cleaning up old configs..."
    find "${TLP_CONFIG}" -maxdepth 1 -name "*-dspc-*.conf" -delete 2>/dev/null || true
}

# Removes old style configuration files generated by this script
cleanup_old_style_configs() {
    log_info "Cleaning up old style configs..."
    find "${TLP_CONFIG}" -maxdepth 1 -name "*-deepin-system-power-control*.conf" -delete 2>/dev/null || true
}

# ==============================================================================
# Config Processing Functions (Updated)
# ==============================================================================

# Processes base config: copy directly
process_base_config() {
    local source_file="$1"
    local filename="$2"
    local dest_file="${TLP_CONFIG}/${filename}"

    cp "$source_file" "$dest_file"
    log_info "Copied base config: $filename -> $dest_file"
}

# Processes mode config: replace 'mode' with actual mode, extract section
process_mode_config() {
    local source_file="$1"
    local filename="$2"
    local mode="$3"

    local dest_filename="${filename/-mode/-${mode}}"
    local dest_file="${TLP_CONFIG}/${dest_filename}"

    log_debug "Processing mode config: $filename -> $dest_filename [mode=$mode]"

    # Step 1: Capture the extracted content
    local content
    content=$(extract_section "$source_file" "$mode")

    # Step 2: Only write file if content is non-empty
    if [[ -n "$content" ]]; then
        printf '%s\n' "$content" > "$dest_file"
        log_info "Generated: $dest_file"
    else
        log_debug "Skipped (no content): $dest_filename"
    fi
}

# Processes override config: check condition, then process base or mode
process_override_config() {
    local source_file="$1"
    local filename="$2"
    local mode="$3"

    local feature="${filename##*@}"
    feature="${feature%.conf}"

    local should_apply=false

    case "$feature" in
        "epp")
            if check_epp; then
                should_apply=true
                log_debug "EPP supported, enabling override: $filename"
            else
                log_debug "EPP not supported, skipping: $filename"
            fi
            ;;
        *)
            log_error "Unknown override feature: $feature"
            ;;
    esac

    if [[ "$should_apply" != "true" ]]; then
        return  # Skip if condition not met
    fi

    # Replace @feature with -feature, and mode with actual mode
    local dest_filename="${filename//-mode/-$mode}"
    dest_filename="${dest_filename//@${feature}/-${feature}}"
    local dest_file="${TLP_CONFIG}/${dest_filename}"

    if [[ "$filename" == *"-override-base"* ]]; then
        cp "$source_file" "$dest_file"
        log_info "Applied override base: $dest_filename -> $dest_file"
    elif [[ "$filename" == *"-override-mode"* ]]; then
        local content
        content=$(extract_section "$source_file" "$mode")
        if [[ -n "$content" ]]; then
            printf '%s\n' "$content" > "$dest_file"
            log_info "Generated: $dest_file"
        else
            log_debug "Skipped (no content): $dest_filename"
        fi
    fi
}

# Main function: processes all configuration files
process_configs() {
    local mode="$1"
    log_info "Processing configuration files for mode: $mode..."

    cleanup_old_configs

    local config_file
    while IFS= read -r -d '' config_file; do
        local filename
        filename=$(basename "$config_file")

        # Skip files that don't contain '-dspc-' as a standalone component
        if [[ ! "$filename" == *-dspc-* ]]; then
            continue
        fi

        case "$filename" in
            *-override-mode@*.conf | *-override-base@*.conf)
                process_override_config "$config_file" "$filename" "$mode"
                ;;
            *-base.conf)
                process_base_config "$config_file" "$filename"
                ;;
            *-mode.conf)
                process_mode_config "$config_file" "$filename" "$mode"
                ;;
            *)
                log_error "Unrecognized dspc config pattern: $filename"
                ;;
        esac
    done < <(find "${TLP_DIR}" -name "*.conf" -print0)

    log_info "Success: Configuration processed for mode '$mode'"
}

# ==============================================================================
# Main Function
# ==============================================================================

run_init_start() {
    log_info "Starting TLP service..."
    tlp init start || {
        log_error "tlp init start failed"
        exit 1
    }
    log_info "Success: TLP service started"
}

main() {
    # Parse -d flag
    while [[ $# -gt 0 && "$1" == "-d" ]]; do
        DEBUG=true
        shift
    done

    # Check if any args left
    if [[ $# -eq 0 ]]; then
        log_error "No command provided"
        usage
        exit 1
    fi

    check_root
    validate_command "$1"
    cleanup_old_style_configs

    case "$1" in
        "set")
            if [[ "$#" -lt 2 ]]; then
                log_error "'set' command requires a mode"
                usage
                exit 1
            fi
            validate_mode "$2"
            process_configs "$2"
            run_init_start
            ;;
        "init-start")
            if [[ "$#" -gt 1 ]]; then
                log_error "'init-start' takes no arguments"
                usage
                exit 1
            fi
            run_init_start
            ;;
        *)
            usage
            exit 1
            ;;
    esac
}

# Entry point
main "$@"
