import SwiftUI
import UIKit

struct OrientationAwareImage: View {
    let imagePath: String
    let contentMode: ContentMode

    @State private var uiImage: UIImage?
    @State private var isLoading = true
    @State private var hasFailed = false

    init(imagePath: String, contentMode: ContentMode = .fit) {
        self.imagePath = imagePath
        self.contentMode = contentMode
    }

    var body: some View {
        Group {
            if let uiImage = uiImage {
                Image(uiImage: uiImage)
                    .resizable()
                    .aspectRatio(contentMode: contentMode)
            } else if hasFailed {
                Image(systemName: "photo")
                    .foregroundColor(.gray)
            } else {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
            }
        }
        .onAppear {
            loadImageWithCorrectOrientation()
        }
        .onChange(of: imagePath) { _, _ in
            loadImageWithCorrectOrientation()
        }
    }

    private func loadImageWithCorrectOrientation() {
        Task.detached(priority: .userInitiated) {
            let correctedImage = await loadAndCorrectImage()
            await MainActor.run {
                self.uiImage = correctedImage
                self.isLoading = false
                self.hasFailed = correctedImage == nil
            }
        }
    }

    private func loadAndCorrectImage() async -> UIImage? {
        guard let data = ImageManager.shared.loadImageData(fromPath: imagePath) else { return nil }

        guard let originalImage = UIImage(data: data) else { return nil }

        return correctImageOrientation(originalImage)
    }

    /// Corrects the orientation of a UIImage based on its EXIF data
    private func correctImageOrientation(_ image: UIImage) -> UIImage {
        // If the image is already in the correct orientation, return as-is
        if image.imageOrientation == .up {
            return image
        }

        // Calculate the proper transformation matrix
        var transform = CGAffineTransform.identity

        switch image.imageOrientation {
        case .down, .downMirrored:
            transform = transform.translatedBy(x: image.size.width, y: image.size.height)
            transform = transform.rotated(by: .pi)

        case .left, .leftMirrored:
            transform = transform.translatedBy(x: image.size.width, y: 0)
            transform = transform.rotated(by: .pi / 2)

        case .right, .rightMirrored:
            transform = transform.translatedBy(x: 0, y: image.size.height)
            transform = transform.rotated(by: -.pi / 2)

        case .up, .upMirrored:
            break

        @unknown default:
            break
        }

        switch image.imageOrientation {
        case .upMirrored, .downMirrored:
            transform = transform.translatedBy(x: image.size.width, y: 0)
            transform = transform.scaledBy(x: -1, y: 1)

        case .leftMirrored, .rightMirrored:
            transform = transform.translatedBy(x: image.size.height, y: 0)
            transform = transform.scaledBy(x: -1, y: 1)

        case .up, .down, .left, .right:
            break

        @unknown default:
            break
        }

        // Create a new image context and apply the transformation
        guard let cgImage = image.cgImage else { return image }

        let context = CGContext(
            data: nil,
            width: Int(image.size.width),
            height: Int(image.size.height),
            bitsPerComponent: cgImage.bitsPerComponent,
            bytesPerRow: 0,
            space: cgImage.colorSpace ?? CGColorSpaceCreateDeviceRGB(),
            bitmapInfo: cgImage.bitmapInfo.rawValue
        )

        guard let ctx = context else { return image }

        ctx.concatenate(transform)

        switch image.imageOrientation {
        case .left, .leftMirrored, .right, .rightMirrored:
            ctx.draw(
                cgImage, in: CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width))
        default:
            ctx.draw(
                cgImage, in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
        }

        guard let newCGImage = ctx.makeImage() else { return image }
        return UIImage(cgImage: newCGImage)
    }
}

// MARK: - Convenience Extensions

extension OrientationAwareImage {
    /// Creates an orientation-aware image with fill content mode
    static func fill(imagePath: String) -> OrientationAwareImage {
        OrientationAwareImage(imagePath: imagePath, contentMode: .fill)
    }

    /// Creates an orientation-aware image with fit content mode
    static func fit(imagePath: String) -> OrientationAwareImage {
        OrientationAwareImage(imagePath: imagePath, contentMode: .fit)
    }
}
