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

import Foundation

class PhpHelper {

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

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

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

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

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

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

                if 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: "\(Paths.optPath)/php@\(version)/bin")
                    .resolvingSymlinksInPath().path

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

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

                    if !FileSystem.isExecutableFile(destination) {
                        try 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(dotless)
                }
            } catch {
                Log.err(error)
                Log.err("Could not write PHP Monitor helper for PHP \(version) to \(destination))")
            }
        }
    }

    private static func zshScript(
        _ 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(
        _ path: String,
        _ keyPhrase: String,
        _ version: String,
        _ dotless: String
    ) -> String {
        return """
            #!\(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(_ dotless: String) async {
        let source = "\(Paths.homePath)/.config/phpmon/bin/pm\(dotless)"
        let destination = "/usr/local/bin/pm\(dotless)"

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

        if !FileSystem.isSymlink(destination) {
            Log.info("Overwriting existing file with new symlink: \(destination)")
            await Shell.quiet("ln -fs \(source) \(destination)")
            return
        }

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