import SwiftUI

public struct OnDeviceFileDataModel: Identifiable, Equatable {
    public enum RootDirectory {
        case documents
        case cache

        var url: URL? {
            let searchDirectory: FileManager.SearchPathDirectory
            switch self {
            case .documents:
                searchDirectory = .documentDirectory
            case .cache:
                searchDirectory = .cachesDirectory
            }
            return FileManager.default.urls(for: searchDirectory, in: .userDomainMask).first
        }

        var displayString: String {
            switch self {
            case .documents:
                return "documents"
            case .cache:
                return "cache"
            }
        }
    }

    enum HighLevelFileType {
        case generic
        case directory
        case movie
        case audio
        case image
        case text
        case archive
        case json

        var icon: String {
            switch self {
            case .generic:
                return "doc"
            case .directory:
                return "folder"
            case .movie:
                return "video"
            case .image:
                return "photo"
            case .json:
                return "curlybraces"
            case .audio:
                return "music.note"
            case .text:
                return "text.document"
            case .archive:
                return "zipper.page"
            }
        }

        static func fromExtension(_ ext: String) -> HighLevelFileType {
            let lowercased = ext.lowercased()
            // This is an incomplete list but it's not really
            // required to make it complete it's just nice to make
            // reasonably representative of common types
            switch lowercased {
            case "mp4", "mov", "avi", "mkv", "flv", "wmv":
                return .movie
            case "mp3", "wav", "flac", "aac", "m4a":
                return .audio
            case "jpeg", "jpg", "png", "gif", "bmp", "tiff", "heic":
                return .image
            case "json":
                return .json
            case "txt", "md", "rtf":
                return .text
            case "zip", "rar", "7z", "tar", "gz":
                return .archive
            default:
                return .generic
            }
        }
    }

    public var id: String {
        return url.path()
    }

    let url: URL
    let highLevelFileType: HighLevelFileType

    var isDirectory: Bool {
        return highLevelFileType == .directory
    }

    var topLevelSortIndex: Int {
        return isDirectory ? 0 : 1
    }

    var lastComponent: String {
        return url.lastPathComponent
    }

    static func directorySelection(root: URL?, subPathSelections: [String]) -> URL? {
        var documentsDirectory: URL? = root

        for subPath in subPathSelections {
            documentsDirectory = documentsDirectory?.appendingPathComponent(subPath)
        }

        return documentsDirectory
    }

    static func loadFileDataModels(root: URL?, subPathSelections: [String]) -> [OnDeviceFileDataModel] {
        let directory = directorySelection(root: root, subPathSelections: subPathSelections)
        var dataModels: [OnDeviceFileDataModel] = []
        directoryContents(directory, dataModels: &dataModels, recursive: false)
        dataModels.sort { $0.topLevelSortIndex < $1.topLevelSortIndex }
        return dataModels
    }

    static func directoryContents(
        _ directory: URL?,
        dataModels: inout [OnDeviceFileDataModel],
        recursive: Bool
    ) {
        do {
            // Retrieve the contents of the directory
            guard let directory else { return }
            let content = try FileManager.default
                .contentsOfDirectory(
                    at: directory,
                    includingPropertiesForKeys: nil,
                    options: []
                )

            for fileURL in content {
                var isDirectory: ObjCBool = false

                if FileManager.default.fileExists(atPath: fileURL.path(), isDirectory: &isDirectory) {
                    if isDirectory.boolValue {
                        dataModels.append(.init(
                            url: fileURL,
                            highLevelFileType: .directory
                        )
                        )
                        if recursive {
                            directoryContents(fileURL, dataModels: &dataModels, recursive: recursive)
                        }
                    } else {
                        dataModels.append(
                            .init(
                                url: fileURL,
                                highLevelFileType: .fromExtension(fileURL.pathExtension)
                            )
                        )
                    }
                }
            }

        } catch {
            print(error)
        }
    }
}
