import SwiftUI
import SFSafeSymbols

private extension Button {

    init(_ symbol: SFSymbol, action: @escaping @MainActor () -> Void) where Label == Image {
        self.init(action: action, label: { Image(systemSymbol: symbol) })
    }
}

private struct ButtonIcon: View {

    let symbol: SFSymbol

    let action: @MainActor () -> Void

    init(_ symbol: SFSymbol, action: @escaping @MainActor () -> Void) {
        self.symbol = symbol
        self.action = action
    }

    var body: some View {
        Button(action: action) {
            Image(systemSymbol: symbol)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(height: 20)
        }
    }
}

struct FileDetailView: View {

    @EnvironmentObject
    private var content: Content

    @EnvironmentObject
    private var selection: SelectedContent

    @Environment(\.language)
    private var language

    @ObservedObject
    var file: FileResource

    var body: some View {
        ScrollView {
            VStack(alignment: .leading) {
                DetailTitle(
                    title: "File",
                    text: "A file that can be used in a post or page")

                GenericPropertyView(title: "Actions") {
                    if file.isExternallyStored {
                        ColoredButton(
                            icon: .squareAndArrowDown,
                            text: "Convert to internal file",
                            action: replaceFile)
                    } else {
                        ColoredButton(icon: .folder, text: "Show in folder", action: showFileInFinder)
                        ColoredButton(icon: .arrowClockwise, text: "Mark file as changed", action: markFileAsChanged)
                        ColoredButton(
                            icon: .arrowLeftArrowRight,
                            text: "Replace file",
                            action: replaceFile)
                        ColoredButton(
                            icon: .squareDashed,
                            text: "Convert to external file",
                            action: convertToExternal)
                    }
                    ColoredButton(delete: deleteFile)
                }

                IdPropertyView(
                    id: $file.identifier,
                    title: "Name",
                    footer: "The unique name of the file, which is also used to reference it in posts and pages.",
                    validation: file.isValid,
                    update: { file.update(id: $0) })
                .id(file.id)

                switch language {
                case .english:
                    OptionalStringPropertyView(
                        title: "Description",
                        text: $file.english,
                        footer: "The description for the file. Descriptions are used for images and to explain the content of a file.")
                case .german:
                    OptionalStringPropertyView(
                        title: "Description",
                        text: $file.german,
                        footer: "The description for the file. Descriptions are used for images and to explain the content of a file.")
                }

                OptionalStringPropertyView(
                    title: "Version",
                    text: $file.version,
                    footer: "The version of this file.")

                OptionalStringPropertyView(
                    title: "Source URL",
                    text: $file.sourceUrl,
                    footer: "The url where this file can be downloaded from.")

                OptionalStringPropertyView(
                    title: "Custom output path",
                    text: $file.customOutputPath,
                    footer: "A custom path where the file is stored in the output folder")

                BoolPropertyView(
                    title: "Asset",
                    value: $file.isAsset,
                    footer: "Indicate that this file should be treated as an asset")

                if let imageDimensions = file.imageDimensions {
                    GenericPropertyView(title: "Image dimensions") {
                        Text("\(Int(imageDimensions.width)) x \(Int(imageDimensions.height)) (\(file.aspectRatio))")
                    }
                }
                if let fileSize = file.fileSize {
                    GenericPropertyView(title: "File size") {
                        Text(formatBytes(fileSize))
                    }
                }
                GenericPropertyView(title: "Added") {
                    Text(file.addedDate.formatted())
                }

                GenericPropertyView(title: "Modified") {
                    Text(file.modifiedDate.formatted())
                }

                if !file.generatedImageVersions.isEmpty {
                    GenericPropertyView(title: "Image versions") {
                        VStack(alignment: .leading) {
                            ForEach(file.generatedImageVersions.sorted(), id: \.self) { version in
                                Text(version)
                            }
                        }
                    }
                }
            }.padding()
        }.onAppear {
            if file.fileSize == nil {
                file.determineFileSize()
            }
        }
    }

    private func formatBytes(_ bytes: Int) -> String {
        let formatter = ByteCountFormatter()
        formatter.allowedUnits = [.useMB, .useKB, .useBytes] // Customize units if needed
        formatter.countStyle = .file
        return formatter.string(fromByteCount: Int64(bytes))
    }

    private func showFileInFinder() {
        content.storage.openFinderWindow(withSelectedFile: file.identifier)
    }

    private func markFileAsChanged() {
        DispatchQueue.main.async {
            file.determineImageDimensions()
            file.determineFileSize()
            // Force regeneration of images and/or file copying
            file.removeFileFromOutputFolder()
            file.modifiedDate = .now
        }
    }

    private func replaceFile() {
        guard let url = openFilePanel() else {
            print("File '\(file.identifier)': No file selected as replacement")
            return
        }
        guard content.storage.importExternalFile(at: url, fileId: file.identifier) else {
            print("File '\(file.identifier)': Failed to replace file")
            return
        }

        markFileAsChanged()
        if file.isExternallyStored {
            DispatchQueue.main.async {
                file.isExternallyStored = false
            }
        }
    }

    private func openFilePanel() -> URL? {
        let panel = NSOpenPanel()

        panel.canChooseFiles = true
        panel.canChooseDirectories = false
        panel.allowsMultipleSelection = false
        panel.showsHiddenFiles = false
        panel.title = "Select file to replace"
        panel.prompt = ""

        let response = panel.runModal()
        guard response == .OK else {
            print("File '\(file.identifier)': Failed to select file to replace")
            return nil
        }

        return panel.url
    }

    private func convertToExternal() {
        guard !file.isExternallyStored else {
            return
        }

        guard content.storage.removeFileContent(file: file.identifier) else {
            print("File '\(file.identifier)': Failed to delete file to make it external")
            return
        }
        DispatchQueue.main.async {
            file.fileSize = nil
            file.isExternallyStored = true
        }
    }

    private func deleteFile() {
        guard content.storage.delete(file: file.identifier) else {
            print("File '\(file.identifier)': Failed to delete file in content folder")
            return
        }
        content.remove(file)
        selection.remove(file)
    }
}

extension FileDetailView: MainContentView {

    init(item: FileResource) {
        self.init(file: item)
    }

    static let itemDescription = "a file"
}


#Preview {
    FileDetailView(file: .mock)
}
