A tmpfiles.d implementation for UNIX-like operating systems.
// dtmp: A tmpfiles.d implementation for UNIX-like operating systems.
// Copyright (C) 2025  Aster Boese
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use crate::{fs::build_dir, parser};

use std::{fs::OpenOptions, io::Write, os::unix::fs::OpenOptionsExt};

pub fn create() -> Result<(), anyhow::Error> {
	let mut configs = Vec::new();
	let config_files = std::fs::read_dir("/usr/lib/tmpfiles.d")?;
	for file in config_files {
		match file {
			Ok(f) => match parser::parse(&f.path()) {
				Ok(config) => configs.push(config),
				Err(e) => anyhow::bail!("Config parsing failed with error:\n{}", e),
			},
			Err(e) => anyhow::bail!("Config parsing failed with error:\n{}", e),
		}
	}

	for config in &configs {
		for line in config {
			match line.file_type.as_str() {
				"f" => {
					match OpenOptions::new()
						.create(true)
						.append(true)
						.mode(u32::from_str_radix(&line.mode, 8).unwrap())
						.open(&line.path.as_os_str())
					{
						Ok(..) => continue,
						Err(e) => anyhow::bail!("File creation failed with error:\n{}", e),
					}
				}
				"f+" => {
					match OpenOptions::new()
						.create(true)
						.append(true)
						.truncate(true)
						.mode(u32::from_str_radix(&line.mode, 8).unwrap())
						.open(&line.path.as_os_str())
					{
						Ok(..) => continue,
						Err(e) => anyhow::bail!("File creation failed with error:\n{}", e),
					}
				}
				"w" => {
					// TODO: Handle if there is a newline
					if !line.argument.contains("\n") {
						let mut file = std::fs::File::open(&line.path.as_os_str()).unwrap();
						match file.write_all(&line.argument.as_bytes()) {
							Ok(..) => continue,
							Err(e) => anyhow::bail!("File creation failed with error:\n{}", e),
						}
					}
				}
				"w+" => {
					// TODO: Handle if there is not a newline
					if line.argument.contains("\n") {
						let mut file = std::fs::File::open(&line.path.as_os_str()).unwrap();
						match file.write_all(&line.argument.as_bytes()) {
							Ok(..) => continue,
							Err(e) => anyhow::bail!("File creation failed with error:\n{}", e),
						}
					}
				}
				"d" => match build_dir(&line.path, u32::from_str_radix(&line.mode, 8).unwrap()) {
					Ok(..) => continue,
					Err(e) => anyhow::bail!("Directory creation failed with error:\n{}", e),
				},
				// TODO: Handle deleting directory contents
				"D" => match build_dir(&line.path, u32::from_str_radix(&line.mode, 8).unwrap()) {
					Ok(..) => continue,
					Err(e) => anyhow::bail!("Directory creation failed with error:\n{}", e),
				},
				"e" => todo!(),
				"v" => todo!(),
				"q" => todo!(),
				"Q" => todo!(),
				"p" => todo!(),
				"p+" => todo!(),
				"L" => todo!(),
				"L+" => todo!(),
				"L?" => todo!(),
				"c" => todo!(),
				"c+" => todo!(),
				"b" => todo!(),
				"b+" => todo!(),
				"C" => todo!(),
				"C+" => todo!(),
				"x" => todo!(),
				"X" => todo!(),
				"r" => todo!(),
				"R" => todo!(),
				"z" => todo!(),
				"Z" => todo!(),
				"t" => todo!(),
				"T" => todo!(),
				"h" => todo!(),
				"H" => todo!(),
				"a" => todo!(),
				"a+" => todo!(),
				"A" => todo!(),
				"A+" => todo!(),
				_ => anyhow::bail!("File type '{}' is not supported.", line.file_type),
			};
		}
	}
	Ok(())
}