//
//  PhpHelper.swift
//  PHP Monitor
//
//  Created by Nico Verbruggen on 17/03/2022.
//  Copyright © 2025 Nico Verbruggen. All rights reserved.
//

import Foundation

class PhpHelper {
    static let keyPhrase = "This file was automatically generated by PHP Monitor."

    public static func generate(
        _ container: Container,
        for version: String
    ) async {
        // Take the PHP version (e.g. "7.2") and generate a dotless version
        let dotless = version.replacing(".", with: "")

        // Determine the dotless name for this PHP version
        let destination = "\(container.paths.homePath)/.config/phpmon/bin/pm\(dotless)"

        // Check if the ~/.config/phpmon/bin directory is in the PATH
        let inPath = container.shell.PATH.contains("\(container.paths.homePath)/.config/phpmon/bin")

        // Check if we can create symlinks (`/usr/local/bin` must be writable)
        let canWriteSymlinks = container.filesystem.isWriteableFile("/usr/local/bin/")

        Task { // Create the appropriate folders and check if the files exist
            do {
                if !container.filesystem.directoryExists("~/.config/phpmon/bin") {
                    Task { @MainActor in
                        try container.filesystem.createDirectory(
                            "~/.config/phpmon/bin",
                            withIntermediateDirectories: true
                        )
                    }
                }

                if container.filesystem.fileExists(destination) {
                    let contents = try String(contentsOfFile: destination)
                    if !contents.contains(keyPhrase) {
                        Log.info("The file at '\(destination)' already exists and was not generated by PHP Monitor "
                                 + "(or is unreadable). Not updating this file.")
                        return
                    }
                }

                // Let's follow the symlink to the PHP binary folder
                let path = URL(fileURLWithPath: "\(container.paths.optPath)/php@\(version)/bin")
                    .resolvingSymlinksInPath().path

                // Check if the user uses Fish
                let script = container.paths.shell.contains("/fish")
                    ? fishScript(container, path, keyPhrase, version, dotless)
                    : zshScript(container, path, keyPhrase, version, dotless)

                Task { @MainActor in
                    try container.filesystem.writeAtomicallyToFile(destination, content: script)

                    if !container.filesystem.isExecutableFile(destination) {
                        try container.filesystem.makeExecutable(destination)
                    }
                }

                // Create a symlink if the folder is not in the PATH
                if !inPath {
                    // First, check if we can create symlinks at all
                    if !canWriteSymlinks {
                        Log.err("PHP Monitor does not have permission to symlink `/usr/local/bin/\(dotless)`.")
                        return
                    }

                    // Write the symlink
                    await self.createSymlink(container, dotless)
                }
            } catch {
                Log.err(error)
                Log.err("Could not write PHP Monitor helper for PHP \(version) to \(destination))")
            }
        }
    }

    private static func zshScript(
        _ container: Container,
        _ path: String,
        _ keyPhrase: String,
        _ version: String,
        _ dotless: String
    ) -> String {
        return """
            #!/bin/zsh
            # \(keyPhrase)
            # It reflects the location of PHP \(version)'s binaries on your system.
            # Usage: . pm\(dotless)
            [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] \\
                && echo "PHP Monitor has enabled this terminal to use PHP \(version)." \\
                || echo "You must run '. pm\(dotless)' (or 'source pm\(dotless)') instead!";
            export PATH=\(path):$PATH
            """
    }

    private static func fishScript(
        _ container: Container,
        _ path: String,
        _ keyPhrase: String,
        _ version: String,
        _ dotless: String,
    ) -> String {
        return """
            #!\(container.paths.binPath)/fish
            # \(keyPhrase)
            # It reflects the location of PHP \(version)'s binaries on your system.
            # Usage: . pm\(dotless)
            echo "PHP Monitor has enabled this terminal to use PHP \(version)."; \\
            set -x PATH \(path) $PATH
            """
    }

    private static func createSymlink(
        _ container: Container,
        _ dotless: String,
    ) async {
        let source = "\(container.paths.homePath)/.config/phpmon/bin/pm\(dotless)"
        let destination = "/usr/local/bin/pm\(dotless)"

        if !container.filesystem.fileExists(destination) {
            Log.info("Creating new symlink: \(destination)")
            await container.shell.quiet("ln -s \(source) \(destination)")
            return
        }

        if !App.shared.container.filesystem.isSymlink(destination) {
            Log.info("Overwriting existing file with new symlink: \(destination)")
            await container.shell.quiet("ln -fs \(source) \(destination)")
            return
        }

        Log.info("Symlink in \(destination) already exists, OK.")
    }
}
