can you revise the following code to fix these bugs?

for 1, the first phrase recording audio functions as expected. all of the following once after that, however, are cut off at the start of the recordings. and then the very last one returns this error: OpenFromDataSource failed
2024-10-08 05:33:50.943515+1100 App Are[97126:4768853]  Open failed
Failed to play audio: The operation couldn’t be completed. (OSStatus error 1685348671.)

also, please remove all trunctuation features from the code, except for the request in the good morning message prompt that requests the message to be under 53 characters. also, please add a feature that creates new cells the same way as the phrases but just for noise in general, like snoring etc except these are not transcribed or sent to the api. they should be labeled as "Noise" please make this changes without causing any bugs:

import UIKit
import AVFoundation
import Speech
import BackgroundTasks

struct SleepTalk: Codable {
    let id: UUID
    var phrase: String
    var interpretation: String?
    var audioURL: URL?
    var date: Date
}

class ViewController: UIViewController, AVAudioPlayerDelegate {
    
    // MARK: - Properties
    
    private var audioEngine: AVAudioEngine?
    private var audioRecorder: AVAudioRecorder?
    private var audioPlayer: AVAudioPlayer?
    private var speechRecognizer: SFSpeechRecognizer?
    private var recognitionRequest: SFSpeechAudioBufferRecognitionRequest?
    private var recognitionTask: SFSpeechRecognitionTask?
    private var sleepTalks: [SleepTalk] = [] {
        didSet {
            saveSleepTalks()
        }
    }
    
    private var goodMorningLabel: UILabel?
    private var goodMorningTimer: Timer?
    
    private var nightPhrases: [String] = []
    
    private let tableView = UITableView()
    private let startButton = UIButton(type: .system)
    private let stopButton = UIButton(type: .system)
    private let testButton = UIButton(type: .system)
    private let statusLabel = UILabel()
    private let dateLabel = UILabel()
    
    private var isTestRecording = false
    private var isSleepRecording = false
    private var currentPhraseID: UUID?
    private var backgroundTask: UIBackgroundTaskIdentifier = .invalid
    private var currentSession: [SleepTalk] = []
    private var silenceTimer: Timer?
    private let silenceThreshold: TimeInterval = 5.0
    private var summaryButton: UIButton!
    private var recordingCount = 0
    private var summary: String?
    
    // MARK: - Lifecycle Methods
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        setupAudioSession()
        setupSpeechRecognizer()
        registerBackgroundTask()
        loadSleepTalks()
        
        // Additional Debugging
        print("View did load, audioEngine initialized: \(audioEngine != nil)")
    }
        
    private func saveSleepTalks() {
        do {
            let encodedData = try JSONEncoder().encode(sleepTalks)
            UserDefaults.standard.set(encodedData, forKey: "sleepTalks")
            print("Successfully saved sleep talks.")
        } catch {
            print("Error saving sleep talks: \(error)")
        }
    }

    private func loadSleepTalks() {
        if let savedData = UserDefaults.standard.data(forKey: "sleepTalks") {
            do {
                sleepTalks = try JSONDecoder().decode([SleepTalk].self, from: savedData)
                tableView.reloadData()
                print("Successfully loaded sleep talks. Count: \(sleepTalks.count)")
            } catch {
                print("Error loading sleep talks: \(error)")
            }
        } else {
            print("No saved sleep talks found.")
        }
    }
    
    private func stopTranscription() {
        print("Stopping transcription only, not the recording.")
        
        // End the current recognition request and task
        recognitionRequest?.endAudio()
        recognitionTask?.cancel()
        recognitionRequest = nil
        recognitionTask = nil
        
        // Remove tap to stop receiving audio buffers for transcription
        audioEngine?.inputNode.removeTap(onBus: 0)
        
        // Stop the audio engine if it's running
        if audioEngine?.isRunning == true {
            audioEngine?.stop()
        }
        
        // **FIX:** Reset currentPhraseID to allow new phrases to be created
        currentPhraseID = nil
        
        print("Transcription stopped. Recording continues.")
    }


    
    

    // MARK: - Setup Methods
    
    private func setupUI() {
        view.backgroundColor = .white
        setupButtons()
        setupStatusLabel()
        setupDateLabel()
        setupTableView()
        setupSummaryButton()
        layoutUI()
    }

    private func startTestRecording() {
        print("Test recording started")
        startRecording(isTest: true)
    }

    @objc private func toggleTestRecording() {
        if isTestRecording {
            stopRecording()
            testButton.setTitle("Test Recording", for: .normal)
        } else {
            startTestRecording()
            testButton.setTitle("Stop Test", for: .normal)
        }
        isTestRecording.toggle()
    }

    private func updateButtonColors() {
        // Update start button color
        startButton.backgroundColor = isSleepRecording ? .systemBlue : .systemGreen
        startButton.setTitleColor(.white, for: .normal)
        // Update stop button color
        stopButton.backgroundColor = .systemRed
        stopButton.setTitleColor(.white, for: .normal)
        // Update test button color
        testButton.backgroundColor = isTestRecording ? .systemGreen : .systemBlue
        testButton.setTitleColor(.white, for: .normal)
    
        // Ensure button actions are correctly set (Avoid multiple target additions)
        testButton.removeTarget(nil, action: nil, for: .allEvents)
        testButton.addTarget(self, action: #selector(toggleTestRecording), for: .touchUpInside)
        startButton.removeTarget(nil, action: nil, for: .allEvents)
        startButton.addTarget(self, action: #selector(startSleepRecording), for: .touchUpInside)
        stopButton.removeTarget(nil, action: nil, for: .allEvents)
        stopButton.addTarget(self, action: #selector(stopRecording), for: .touchUpInside)
    }

    private func setupStatusLabel() {
        statusLabel.text = "Ready to start"
        statusLabel.font = UIFont.systemFont(ofSize: 16, weight: .regular) // Adjusted font size for clarity
        statusLabel.textAlignment = .center
        statusLabel.textColor = UIColor.darkGray // Subtle color for status label
        statusLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(statusLabel)
    }

    private func setupDateLabel() {
        dateLabel.text = getCurrentDate()
        dateLabel.font = UIFont.systemFont(ofSize: 16, weight: .regular) // Consistent font styling
        dateLabel.textAlignment = .center
        dateLabel.textColor = UIColor.darkGray
        dateLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(dateLabel)
    }

    private func setupTableView() {
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.register(SleepTalkCell.self, forCellReuseIdentifier: "SleepTalkCell")
        tableView.dataSource = self
        tableView.delegate = self
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 100
        view.addSubview(tableView)
    }

    private func setupSummaryButton() {
        summaryButton = UIButton(type: .system)
        summaryButton.setTitle("View Summary", for: .normal)
        summaryButton.backgroundColor = .systemPurple
        summaryButton.setTitleColor(.white, for: .normal)
        summaryButton.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold) // Adjusted font
        summaryButton.layer.cornerRadius = 8 // Softer corners for consistency
        summaryButton.translatesAutoresizingMaskIntoConstraints = false
        summaryButton.addTarget(self, action: #selector(showSummary), for: .touchUpInside)
        view.addSubview(summaryButton)

        NSLayoutConstraint.activate([
            summaryButton.topAnchor.constraint(equalTo: dateLabel.bottomAnchor, constant: 20),
            summaryButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            summaryButton.widthAnchor.constraint(equalToConstant: 150),
            summaryButton.heightAnchor.constraint(equalToConstant: 50)
        ])
    }

    private func layoutUI() {
        NSLayoutConstraint.activate([
            // Start Button
            startButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
            startButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            startButton.widthAnchor.constraint(equalTo: stopButton.widthAnchor), // Match width of stop button
            startButton.heightAnchor.constraint(equalToConstant: 50),

            // Stop Button
            stopButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
            stopButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            stopButton.widthAnchor.constraint(equalToConstant: 150),
            stopButton.heightAnchor.constraint(equalToConstant: 50),

            // Test Button
            testButton.topAnchor.constraint(equalTo: startButton.bottomAnchor, constant: 20),
            testButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            testButton.widthAnchor.constraint(equalToConstant: 200),
            testButton.heightAnchor.constraint(equalToConstant: 50),

            // Status Label
            statusLabel.topAnchor.constraint(equalTo: testButton.bottomAnchor, constant: 20),
            statusLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            statusLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            statusLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),

            // Date Label
            dateLabel.topAnchor.constraint(equalTo: statusLabel.bottomAnchor, constant: 10),
            dateLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            dateLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            dateLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),

            // Table View
            tableView.topAnchor.constraint(equalTo: summaryButton.bottomAnchor, constant: 20),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }

    
    private func setupAudioSession() {
        do {
            try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [.mixWithOthers, .allowBluetooth, .defaultToSpeaker])
            try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
            print("Audio session successfully set up.")
        } catch {
            print("Failed to set up audio session: \(error)")
        }
    }

    private func setupSpeechRecognizer() {
        speechRecognizer = SFSpeechRecognizer(locale: Locale(identifier: "en-US"))
        speechRecognizer?.delegate = self

        SFSpeechRecognizer.requestAuthorization { [weak self] authStatus in
            DispatchQueue.main.async {
                self?.updateUIForSpeechStatus(authStatus)
            }
        }
    }

    private func updateUIForSpeechStatus(_ status: SFSpeechRecognizerAuthorizationStatus) {
        switch status {
        case .authorized:
            statusLabel.text = "Speech recognition authorized"
            startButton.isEnabled = true
        case .denied:
            statusLabel.text = "Speech recognition authorization denied"
            startButton.isEnabled = false
        case .restricted:
            statusLabel.text = "Speech recognition restricted on this device"
            startButton.isEnabled = false
        case .notDetermined:
            statusLabel.text = "Speech recognition not yet authorized"
            startButton.isEnabled = false
        @unknown default:
            statusLabel.text = "Unknown authorization status"
            startButton.isEnabled = false
        }
    }

    
    func performRequestWithExponentialBackoff(request: URLRequest, retryCount: Int = 0, maxRetries: Int = 5, completion: @escaping (Result<Data, Error>) -> Void) {
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            if let error = error {
                if retryCount < maxRetries {
                    let delay = pow(2.0, Double(retryCount))
                    print("Request failed with error: \(error). Retrying in \(delay) seconds...")
                    DispatchQueue.global().asyncAfter(deadline: .now() + delay) {
                        self.performRequestWithExponentialBackoff(request: request, retryCount: retryCount + 1, maxRetries: maxRetries, completion: completion)
                    }
                } else {
                    completion(.failure(error))
                }
                return
            }
            
            if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 429 {
                if retryCount < maxRetries {
                    let delay = pow(2.0, Double(retryCount))
                    print("Received 429 Rate Limit. Retrying in \(delay) seconds...")
                    DispatchQueue.global().asyncAfter(deadline: .now() + delay) {
                        self.performRequestWithExponentialBackoff(request: request, retryCount: retryCount + 1, maxRetries: maxRetries, completion: completion)
                    }
                } else {
                    let rateLimitError = NSError(domain: "APIErrorDomain", code: 429, userInfo: [NSLocalizedDescriptionKey: "Rate limit exceeded. Please try again later."])
                    completion(.failure(rateLimitError))
                }
                return
            }
            
            guard let data = data else {
                let noDataError = NSError(domain: "APIErrorDomain", code: -1, userInfo: [NSLocalizedDescriptionKey: "No data received from API"])
                completion(.failure(noDataError))
                return
            }
            
            completion(.success(data))
        }
        
        task.resume()
    }
    
    // MARK: - Background Task Management
    
    func registerBackgroundTask() {
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.yourapp.sleeprecording", using: nil) { task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }
    }

    func scheduleAppRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: "com.yourapp.sleeprecording")
        request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // Fetch no earlier than 15 minutes from now
        
        do {
            try BGTaskScheduler.shared.submit(request)
        } catch {
            print("Could not schedule app refresh: \(error)")
        }
    }

    func handleAppRefresh(task: BGAppRefreshTask) {
        scheduleAppRefresh()
        
        // Start recording in background
        startBackgroundRecording()
        
        task.setTaskCompleted(success: true)
        
        // Add an expiration handler for when the task times out
        task.expirationHandler = {
            // Call the method to stop the background recording if the task expires
            self.stopBackgroundRecording()
            task.setTaskCompleted(success: false)
        }
    }

    private func startBackgroundRecording() {
        if audioEngine == nil {
            startRecording(isTest: false)
        }
    }

    private func stopBackgroundRecording() {
        if audioEngine != nil {
            stopRecording()
        }
    }
    
    private func handleRecognitionResult(_ result: SFSpeechRecognitionResult?, error: Error?) {
        if let result = result {
            let transcribedText = result.bestTranscription.formattedString
            print("Transcribed Text: \(transcribedText)")
            
            // Only proceed if there's new transcribed text
            if !transcribedText.isEmpty {
                updateOrCreateSleepTalk(text: transcribedText)
                resetSilenceTimer()
            } else {
                print("Received an empty transcription result.")
            }
        }
        
        if let error = error {
            let nsError = error as NSError
            if nsError.domain == "kAFAssistantErrorDomain" && nsError.code == 216 {
                // **FIX:** Handle specific error by restarting transcription
                print("Recognition task error: \(error.localizedDescription)")
                stopTranscription()
                startTranscription()
            } else {
                print("Recognition task error: \(error.localizedDescription)")
                // Optionally, handle specific errors or update UI
                // Do not stop the recording here for other errors
            }
        }
    }

    // Set up the silence detection with a timer to manage transcription only
    private func resetSilenceTimer() {
        silenceTimer?.invalidate() // Reset any previous timers
        silenceTimer = Timer.scheduledTimer(withTimeInterval: silenceThreshold, repeats: false) { [weak self] _ in
            self?.endCurrentPhrase() // Stop transcription but NOT the recording
        }
    }

    // Handles ending the current transcription phrase when silence is detected
    private func endCurrentPhrase() {
        print("Ending current transcription phrase due to silence.")
        
        // Stop only the transcription
        stopTranscription()
        
        // Optionally, allow restarting transcription after a brief pause
        if isSleepRecording || isTestRecording {
            // Wait for a moment before restarting transcription to avoid rapid toggling
            DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
                self?.startTranscription()
                print("Resumed transcription after silence.")
            }
        }
    }

    private func startTranscription() {
        guard let speechRecognizer = speechRecognizer, speechRecognizer.isAvailable else {
            print("Speech recognizer is unavailable")
            statusLabel.text = "Speech recognizer is unavailable"
            return
        }
        
        recognitionRequest = SFSpeechAudioBufferRecognitionRequest()
        
        guard let recognitionRequest = recognitionRequest else {
            print("Unable to create speech recognition request")
            statusLabel.text = "Unable to create speech recognition request"
            return
        }
        
        recognitionRequest.shouldReportPartialResults = true
        
        let inputNode = audioEngine!.inputNode
        let recordingFormat = inputNode.outputFormat(forBus: 0)
        
        inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { [weak self] buffer, when in
            self?.recognitionRequest?.append(buffer)
        }
        
        recognitionTask = speechRecognizer.recognitionTask(with: recognitionRequest) { [weak self] result, error in
            if let result = result {
                self?.handleRecognitionResult(result, error: error)
            }
            
            if error != nil {
                // Errors are handled in handleRecognitionResult
                // Do not perform additional actions here
            }
        }
        
        do {
            try audioEngine!.start()
            print("Audio engine started for transcription")
        } catch {
            print("Audio engine couldn't start for transcription: \(error.localizedDescription)")
            statusLabel.text = "Audio engine start failed"
        }
    }

    private func addToTable(currentPhrase: UUID) {
        let newPhrase = SleepTalk(id: currentPhrase, phrase: "", interpretation: nil, audioURL: nil, date: Date())
        sleepTalks.append(newPhrase)
        tableView.reloadData() // Add a new entry for each phrase
    }


    private func updateUIForRecordingState(isRecording: Bool, isTest: Bool) {
        startButton.isEnabled = !isRecording
        stopButton.isEnabled = isRecording
        isTestRecording = isTest && isRecording
        isSleepRecording = !isTest && isRecording
        statusLabel.text = isRecording ? (isTest ? "Test recording..." : "Recording sleep talk...") : "Recording stopped."
        // **FIX:** No need to reset currentPhraseID here; it's handled in stopTranscription()
    }
    
   
    // Function to update and wrap the good morning message with a 53-character limit
    // The 53-character limit is only enforced in the updateGoodMorningMessage function
    private func updateGoodMorningMessage(with message: String) {
        let truncatedMessage = String(message.prefix(53)) // Limit to 53 characters

        goodMorningLabel?.removeFromSuperview() // Clear previous message
        goodMorningLabel = UILabel()
        goodMorningLabel?.text = truncatedMessage
        goodMorningLabel?.font = UIFont.boldSystemFont(ofSize: 24)
        goodMorningLabel?.textAlignment = .center
        goodMorningLabel?.numberOfLines = 0
        goodMorningLabel?.lineBreakMode = .byWordWrapping
        goodMorningLabel?.textColor = .black
        goodMorningLabel?.backgroundColor = .clear
        goodMorningLabel?.alpha = 0
        goodMorningLabel?.translatesAutoresizingMaskIntoConstraints = false

        // Add to view
        if let goodMorningLabel = goodMorningLabel {
            view.addSubview(goodMorningLabel)
            
            NSLayoutConstraint.activate([
                goodMorningLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
                goodMorningLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
                goodMorningLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
            ])
            
            // Force layout to calculate size
            view.layoutIfNeeded()
            
            // Calculate the required height
            let maxWidth = view.frame.width - 40 // 20 (leading) + 20 (trailing)
            let messageHeight = truncatedMessage.height(withConstrainedWidth: maxWidth, font: goodMorningLabel.font)
            
            let padding: CGFloat = 20
            let totalTranslation = messageHeight + padding
            
            // Animate UI elements to slide down and show message
            UIView.animate(withDuration: 0.5, animations: {
                self.startButton.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.stopButton.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.testButton.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.summaryButton.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.statusLabel.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.dateLabel.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.tableView.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                goodMorningLabel.alpha = 1.0
            }) { _ in
                // After showing the message, set a timer to hide it after 10 seconds
                Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false) { _ in
                    UIView.animate(withDuration: 0.5, animations: {
                        goodMorningLabel.alpha = 0.0
                        self.startButton.transform = .identity
                        self.stopButton.transform = .identity
                        self.testButton.transform = .identity
                        self.summaryButton.transform = .identity
                        self.statusLabel.transform = .identity
                        self.dateLabel.transform = .identity
                        self.tableView.transform = .identity
                    }) { _ in
                        goodMorningLabel.removeFromSuperview()
                    }
                }
            }
        }
    }

    private func setupButtons() {
        [startButton, stopButton, testButton].forEach { button in
            button.layer.removeAllAnimations() // Remove any existing animations
            button.layer.cornerRadius = 8
            button.translatesAutoresizingMaskIntoConstraints = false
            button.titleLabel?.font = UIFont.systemFont(ofSize: 18, weight: .semibold)
            button.titleLabel?.numberOfLines = 2
            button.titleLabel?.textAlignment = .center
            view.addSubview(button)

            // Manually set static colors
            if button == startButton {
                button.backgroundColor = .systemGreen
            } else if button == stopButton {
                button.backgroundColor = .systemRed
            } else if button == testButton {
                button.backgroundColor = .systemBlue
            }
        }

        startButton.setTitle("Start Sleep Recording", for: .normal)
        startButton.setTitleColor(.white, for: .normal)

        stopButton.setTitle("Stop Recording", for: .normal)
        stopButton.setTitleColor(.white, for: .normal)

        testButton.setTitle("Test Recording", for: .normal)
        testButton.setTitleColor(.white, for: .normal)

        // Add targets for buttons
        startButton.addTarget(self, action: #selector(startSleepRecording), for: .touchUpInside)
        stopButton.addTarget(self, action: #selector(stopRecording), for: .touchUpInside)
        testButton.addTarget(self, action: #selector(toggleTestRecording), for: .touchUpInside)
    }

 
 
    @objc private func stopRecording() {
        print("Stop recording triggered")
        
        // Stop silence timer
        silenceTimer?.invalidate()
        silenceTimer = nil
        
        // Stop transcription if it's active
        if recognitionTask != nil {
            stopTranscription()
        }
        
        // Stop audio engine
        if audioEngine?.isRunning == true {
            audioEngine?.stop()
            audioEngine?.inputNode.removeTap(onBus: 0)
        }
        
        // Stop and save the current audio recorder
        if let currentID = currentPhraseID, let index = sleepTalks.firstIndex(where: { $0.id == currentID }) {
            stopCurrentAudioRecording(for: index)
        }
        
        // Update UI to reflect the stopped state
        updateUIForRecordingState(isRecording: false, isTest: isTestRecording)
        
        // **Log the current state of nightPhrases before generating the message**
        print("Final nightPhrases: \(nightPhrases)")
        
        // Display the Good Morning message after recording
        if !nightPhrases.isEmpty {
            generateGoodMorningMessage(for: nightPhrases)
        } else {
            // Fallback message if no phrases
            showGoodMorningMessage("Good Morning!")
        }
        
        // Clear phrases after the session
        nightPhrases.removeAll()
        print("nightPhrases cleared after generating the Good Morning message.")
    }
    
    private func logAudioFileProperties(at url: URL) {
        do {
            let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
            if let fileSize = attributes[.size] as? UInt64,
               let creationDate = attributes[.creationDate] as? Date {
                print("Audio file at \(url.lastPathComponent):")
                print(" - Size: \(fileSize) bytes")
                print(" - Created: \(creationDate)")
            }
        } catch {
            print("Failed to get file attributes for \(url.path): \(error)")
        }
    }
    
    private func stopCurrentAudioRecording(for index: Int) {
        if let recorder = audioRecorder, recorder.isRecording {
            recorder.stop()
            print("Stopped audio recording for SleepTalk ID: \(sleepTalks[index].id)")
            logAudioFileSize(at: sleepTalks[index].audioURL!)
        }
        audioRecorder = nil
    }
    
    private func logAudioFileSize(at url: URL) {
        do {
            let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
            if let fileSize = attributes[.size] as? UInt64 {
                print("Audio file at \(url.lastPathComponent) has size: \(fileSize) bytes")
            }
        } catch {
            print("Failed to get file attributes for \(url.path): \(error)")
        }
    }

    // MARK: - Recording Methods
    
    private func showGoodMorningMessage(_ message: String) {
        print("Displaying Good Morning message: \(message)")
        
        // Remove existing label if any
        goodMorningLabel?.removeFromSuperview()
        
        // Create new label
        goodMorningLabel = UILabel()
        goodMorningLabel?.text = message
        goodMorningLabel?.font = UIFont.boldSystemFont(ofSize: 24)
        goodMorningLabel?.textAlignment = .center
        goodMorningLabel?.numberOfLines = 0
        goodMorningLabel?.textColor = .black
        goodMorningLabel?.backgroundColor = .clear
        goodMorningLabel?.alpha = 0
        goodMorningLabel?.translatesAutoresizingMaskIntoConstraints = false
        
        // Add to view
        if let goodMorningLabel = goodMorningLabel {
            view.addSubview(goodMorningLabel)
            
            NSLayoutConstraint.activate([
                goodMorningLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
                goodMorningLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
                goodMorningLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
            ])
            
            // Force layout to calculate size
            view.layoutIfNeeded()
            
            // Calculate the required height
            let maxWidth = view.frame.width - 40 // 20 (leading) + 20 (trailing)
            let messageHeight = message.height(withConstrainedWidth: maxWidth, font: goodMorningLabel.font)
            
            let padding: CGFloat = 20
            let totalTranslation = messageHeight + padding
            
            // Animate UI elements to slide down and show message
            UIView.animate(withDuration: 0.5, animations: {
                self.startButton.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.stopButton.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.testButton.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.summaryButton.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.statusLabel.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.dateLabel.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                self.tableView.transform = CGAffineTransform(translationX: 0, y: totalTranslation)
                goodMorningLabel.alpha = 1.0
            }) { _ in
                // After showing the message, set a timer to hide it after 10 seconds
                Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false) { _ in
                    UIView.animate(withDuration: 0.5, animations: {
                        goodMorningLabel.alpha = 0.0
                        self.startButton.transform = .identity
                        self.stopButton.transform = .identity
                        self.testButton.transform = .identity
                        self.summaryButton.transform = .identity
                        self.statusLabel.transform = .identity
                        self.dateLabel.transform = .identity
                        self.tableView.transform = .identity
                    }) { _ in
                        goodMorningLabel.removeFromSuperview()
                    }
                }
            }
        }
    }
    
    @objc private func startSleepRecording() {
        statusLabel.text = "Will start recording in 30 minutes..."
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) { // **NOTE:** Originally 30 minutes, changed to 2 seconds for testing
            self.startRecording(isTest: false)
            self.scheduleAppRefresh()
        }
    }

    // MARK: - Recording Methods

    private func startRecording(isTest: Bool) {
        print("Starting recording, isTest: \(isTest)")
        
        // Update UI to reflect recording state
        updateUIForRecordingState(isRecording: true, isTest: isTest)
        
        // Initialize AVAudioEngine if not already
        if audioEngine == nil {
            audioEngine = AVAudioEngine()
        }
        
        // Create the first SleepTalk entry
        let newID = UUID()
        currentPhraseID = newID
        let newAudioURL = getNewAudioURL()
        let newSleepTalk = SleepTalk(id: newID, phrase: "", interpretation: nil, audioURL: newAudioURL, date: Date())
        sleepTalks.append(newSleepTalk)
        print("Added new SleepTalk with ID: \(newID) for recording.")
        
        // Initialize AVAudioRecorder for the new SleepTalk
        startAudioRecording(for: newSleepTalk)
        
        // Start transcription
        startTranscription()
        
        // Schedule silence detection timer
        silenceTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
            self?.checkForSilence()
        }
        
        print("Recording successfully started")
    }


    private func checkForSilence() {
        guard let audioRecorder = audioRecorder else { return }
        
        audioRecorder.updateMeters()
        let audioLevel = audioRecorder.averagePower(forChannel: 0)
        print("Current Audio Level: \(audioLevel)")
        
        if audioLevel < -40 {  // Silence threshold (adjustable)
            // If not already counting silence, start the timer
            if silenceTimer == nil {
                silenceTimer = Timer.scheduledTimer(withTimeInterval: silenceThreshold, repeats: false) { [weak self] _ in
                    self?.endCurrentPhrase()
                }
                print("Silence detected, starting timer to end phrase")
            }
        } else {
            // Audio is detected, reset silence timer
            if silenceTimer != nil {
                silenceTimer?.invalidate()
                silenceTimer = nil
                print("Audio detected, resetting silence timer")
            }
        }
    }

    
    // MARK: - LLM Integration
    

    private func getNewAudioURL() -> URL {
        let uuid = UUID().uuidString
        let url = getDocumentsDirectory().appendingPathComponent("sleepTalk_\(uuid).m4a")
        print("Generated new audio URL: \(url.path)")
        return url
    }

    // Start recording audio for a specific SleepTalk
    private func startAudioRecording(for sleepTalk: SleepTalk) {
        guard let audioURL = sleepTalk.audioURL else {
            print("Invalid audio URL for SleepTalk ID: \(sleepTalk.id)")
            return
        }
        let settings: [String: Any] = [
            AVFormatIDKey: Int(kAudioFormatLinearPCM),
            AVSampleRateKey: 44100,
            AVNumberOfChannelsKey: 1, // Mono
            AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
        ]
        
        do {
            audioRecorder = try AVAudioRecorder(url: audioURL, settings: settings)
            audioRecorder?.delegate = self
            audioRecorder?.isMeteringEnabled = true
            audioRecorder?.record()
            print("Audio recorder started recording for SleepTalk ID: \(sleepTalk.id) at URL: \(audioURL.path)")
        } catch {
            print("Audio recording failed: \(error.localizedDescription)")
            statusLabel.text = "Audio recording failed"
        }
    }


    
    private func updateOrCreateSleepTalk(text: String) {
        let trimmedText = text.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmedText.isEmpty else {
            print("Received empty or whitespace-only phrase. Ignoring.")
            return  // Avoid adding empty phrases
        }
        
        if let currentID = currentPhraseID, let index = sleepTalks.firstIndex(where: { $0.id == currentID }) {
            // Update existing phrase
            sleepTalks[index].phrase = trimmedText
            print("Updated existing SleepTalk with ID: \(currentID)")
            
            // Since we've updated the phrase, send it to LLM for interpretation
            sendToLLM(text: trimmedText, id: currentID)
        } else {
            // **Ensure any existing recorder is stopped before starting a new one**
            if let recorder = audioRecorder, recorder.isRecording {
                recorder.stop()
                audioRecorder = nil
                print("Stopped previous audio recorder before starting a new one.")
            }
            
            // Create a new SleepTalk with its own audio file
            let newID = UUID()
            currentPhraseID = newID
            let newAudioURL = getNewAudioURL()
            let newSleepTalk = SleepTalk(id: newID, phrase: trimmedText, interpretation: nil, audioURL: newAudioURL, date: Date())
            sleepTalks.append(newSleepTalk)
            print("Added new SleepTalk with ID: \(newID) and phrase: \(trimmedText)")
            
            // Start a new audio recording for this SleepTalk
            startAudioRecording(for: newSleepTalk)
            
            // Also add to nightPhrases
            nightPhrases.append(trimmedText)
            print("Appended phrase to nightPhrases: \(trimmedText)")
        }
        
        DispatchQueue.main.async {
            self.tableView.reloadData()
            print("Table view reloaded. Total sleepTalks: \(self.sleepTalks.count)")
        }
    }
    
    func sendToLLM(text: String, id: UUID) {
        let apiUrl = URL(string: "https://api.groq.com/openai/v1/chat/completions")!
        
        var request = URLRequest(url: apiUrl)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("Bearer \(APIKeys.openRouterKey)", forHTTPHeaderField: "Authorization")
        request.addValue("Your-App-Name", forHTTPHeaderField: "HTTP-Referer")

        let prompt = """
        Analyze the following sleep talk phrase:

        "\(text)"

        Provide an engaging and slightly witty interpretation that briefly analyses and looks into underlying emotions, subconscious thoughts, and any intriguing patterns. Aim for a tone that's both understanding and entertaining. Do not write more than one small-medium paragraph, under any circumstances. Do not include any prefixes, labels or quotation marks, just the raw interpretation.
        """

        let requestBody: [String: Any] = [
            "model": "llama3-8b-8192",
            "messages": [
                ["role": "system", "content": "You are an AI assistant specialized in interpreting sleep talk."],
                ["role": "user", "content": prompt]
            ],
            "temperature": 0.7,
            "max_tokens": 150
        ]

        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: requestBody)
        } catch {
            print("Failed to serialize request body: \(error.localizedDescription)")
            updateInterpretation(for: id, with: "Error: Invalid request format")
            return
        }

        performRequestWithExponentialBackoff(request: request) { [weak self] result in
            DispatchQueue.main.async {
                switch result {
                case .success(let data):
                    // **Log the raw JSON response for debugging**
                    if let rawResponse = String(data: data, encoding: .utf8) {
                        print("Raw API Response: \(rawResponse)")
                    } else {
                        print("Unable to convert data to string for logging.")
                    }

                    do {
                        if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                            // **Handle potential error messages from the API**
                            if let errorResponse = json["error"] as? [String: Any],
                               let message = errorResponse["message"] as? String {
                                print("API Error Response: \(message)")
                                self?.updateInterpretation(for: id, with: "Error: \(message)")
                                return
                            }
                            
                            guard let choices = json["choices"] as? [[String: Any]],
                                  let firstChoice = choices.first,
                                  let message = firstChoice["message"] as? [String: Any],
                                  let content = message["content"] as? String else {
                                self?.updateInterpretation(for: id, with: "Error: Invalid response format")
                                return
                            }
                            
                            let interpretation = content.trimmingCharacters(in: .whitespacesAndNewlines)
                            
                            // Update UI with AI-generated interpretation
                            self?.updateInterpretation(for: id, with: interpretation)
                        } else {
                            self?.updateInterpretation(for: id, with: "Error: Invalid response structure")
                        }
                    } catch {
                        print("JSON parsing error: \(error.localizedDescription)")
                        self?.updateInterpretation(for: id, with: "Error: Unable to parse response")
                    }
                    
                case .failure(let error):
                    print("API request failed: \(error.localizedDescription)")
                    self?.updateInterpretation(for: id, with: "Error: \(error.localizedDescription)")
                }
            }
        }
    }
    
    
    private func generateGoodMorningMessage(for phrases: [String]) {
        guard !phrases.isEmpty else {
            print("No phrases available to generate Good Morning message.")
            showGoodMorningMessage("Good morning! Hope you had interesting dreams.")
            return
        }
        
        let allPhrases = phrases.joined(separator: "\n")
        let prompt = """
        Based on the following sleep talk phrases from last night, craft a witty, engaging, and relevant 'Good Morning' message that MUST be under 52 characters. The message should creatively reference the overall theme or a standout phrase from the night's sleep talk, making it feel personalized and insightful. As a strict rule, always make sure the Good Morning message is relevant to the phrases and reference them in some accurate way, whether its through a joke that uses some of the same words or an uplifiting message referencing specific things they said, you must make it relevant. Do not include any prefixes, labels or quotation marks, just the raw interpretation.

        Sleep talk phrases:
        \(allPhrases)
        """
        
        print("Generating Good Morning message with prompt: \(prompt)")
        
        let apiUrl = URL(string: "https://api.groq.com/openai/v1/chat/completions")!
        
        var request = URLRequest(url: apiUrl)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("Bearer \(APIKeys.openRouterKey)", forHTTPHeaderField: "Authorization")
        request.addValue("Your-App-Name", forHTTPHeaderField: "HTTP-Referer")
        
        let requestBody: [String: Any] = [
            "model": "llama3-8b-8192",
            "messages": [
                ["role": "system", "content": "You are an AI assistant specialized in generating witty good morning messages based on sleep talk."],
                ["role": "user", "content": prompt]
            ],
            "temperature": 0.7,
            "max_tokens": 60
        ]
        
        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: requestBody)
        } catch {
            print("Failed to serialize Good Morning request body: \(error.localizedDescription)")
            showGoodMorningMessage("Good morning! Hope you had interesting dreams.")
            return
        }
        
        performRequestWithExponentialBackoff(request: request) { [weak self] result in
            DispatchQueue.main.async {
                switch result {
                case .success(let data):
                    // **Log the raw JSON response for debugging**
                    if let rawResponse = String(data: data, encoding: .utf8) {
                        print("Raw Good Morning API Response: \(rawResponse)")
                    } else {
                        print("Unable to convert Good Morning data to string for logging.")
                    }
                    
                    do {
                        if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                            // **Handle potential error messages from the API**
                            if let errorResponse = json["error"] as? [String: Any],
                               let message = errorResponse["message"] as? String {
                                print("API Error Response (Good Morning): \(message)")
                                self?.showGoodMorningMessage("Good morning! Let's try again later.")
                                return
                            }
                            
                            guard let choices = json["choices"] as? [[String: Any]],
                                  let firstChoice = choices.first,
                                  let message = firstChoice["message"] as? [String: Any],
                                  let content = message["content"] as? String else {
                                self?.showGoodMorningMessage("Good morning! Have a great day ahead.")
                                print("Invalid response format from Good Morning API")
                                return
                            }
                            
                            let goodMorningMessage = content.trimmingCharacters(in: .whitespacesAndNewlines)
                            self?.showGoodMorningMessage(goodMorningMessage)
                            
                           
                            
                            
                        } else {
                            self?.showGoodMorningMessage("Good morning! Have a great day ahead.")
                            print("Invalid response structure from Good Morning API")
                        }
                    } catch {
                        print("JSON parsing error for Good Morning message: \(error.localizedDescription)")
                        self?.showGoodMorningMessage("Good morning! Your dreams were beyond words.")
                    }
                    
                case .failure(let error):
                    print("Good Morning API request failed: \(error.localizedDescription)")
                    self?.showGoodMorningMessage("Good morning! Let's try again later.")
                }
            }
        }
    }

    private func updateInterpretation(for id: UUID, with interpretation: String) {
        if let index = sleepTalks.firstIndex(where: { $0.id == id }) {
            sleepTalks[index].interpretation = interpretation
            DispatchQueue.main.async {
                self.tableView.reloadData()
                // If DetailViewController is currently presented, update its interpretation label
                if let detailVC = self.presentedViewController as? DetailViewController {
                    detailVC.updateInterpretation(id: id, interpretation: interpretation)
                }
            }
        }
    }
    
    
    private func incrementRecordingCount() {
        recordingCount += 1
        if recordingCount % 15 == 0 {
            generateSummary()
        }
    }

    private func generateSummary() {
        let allPhrases = sleepTalks.map { $0.phrase }.joined(separator: "\n")
        let prompt = """
        Analyze the following sleep talk phrases and provide a summary:
        \(allPhrases)

        Please provide a summary that includes:
        1. Common themes or topics
        2. Overall emotional state or mood
        3. Any recurring patterns or unusual observations
        4. Potential insights into the speaker's subconscious thoughts
        5. Suggestions for improving sleep quality based on the sleep talk content
        """

        let apiUrl = URL(string: "https://api.groq.com/openai/v1/chat/completions")!
        var request = URLRequest(url: apiUrl)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("Bearer \(APIKeys.openRouterKey)", forHTTPHeaderField: "Authorization")
        request.addValue("Your-App-Name", forHTTPHeaderField: "HTTP-Referer")

        let requestBody: [String: Any] = [
            "model": "llama3-8b-8192",
            "messages": [
                ["role": "system", "content": "You are an AI assistant specialized in analyzing sleep talk patterns."],
                ["role": "user", "content": prompt]
            ]
        ]

        request.httpBody = try? JSONSerialization.data(withJSONObject: requestBody)

        let task = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in
            DispatchQueue.main.async {
                if let error = error {
                    print("API request error: \(error)")
                    self?.summary = "Error: Unable to generate summary"
                    return
                }

                guard let data = data else {
                    print("No data received from API")
                    self?.summary = "Error: No data received"
                    return
                }

                do {
                    if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
                       let choices = json["choices"] as? [[String: Any]],
                       let firstChoice = choices.first,
                       let message = firstChoice["message"] as? [String: Any],
                       let summaryText = message["content"] as? String {
                        self?.summary = summaryText
                    } else {
                        self?.summary = "Error: Invalid response format"
                    }
                } catch {
                    print("JSON parsing error: \(error)")
                    self?.summary = "Error: Unable to parse response"
                }
            }
        }

        task.resume()
    }

    func deleteSleepTalk(id: UUID) {
        if let index = sleepTalks.firstIndex(where: { $0.id == id }) {
            sleepTalks.remove(at: index)
            // Reload the table view to reflect the deletion
            tableView.reloadData()
        } else {
            print("Unable to delete: No sleep talk entry found with the given ID.")
        }
    }
    
    @objc private func showSummary() {
        let summaryVC = SummaryViewController(summary: summary ?? "No summary available yet.")
        let navController = UINavigationController(rootViewController: summaryVC)
        present(navController, animated: true, completion: nil)
    }
        // MARK: - Helper Methods
        
        private func getDocumentsDirectory() -> URL {
            FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        }
        
        private func getCurrentDate() -> String {
            let dateFormatter = DateFormatter()
            dateFormatter.dateStyle = .medium
            dateFormatter.timeStyle = .short
            return dateFormatter.string(from: Date())
        }
}

// MARK: - Extensions

// Extension to calculate string height based on width and font
extension String {
    func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(
            with: constraintRect,
            options: [.usesLineFragmentOrigin, .usesFontLeading],
            attributes: [.font: font],
            context: nil
        )
        return ceil(boundingBox.height)
    }
}



// MARK: - UITableViewDataSource & UITableViewDelegate

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print("Number of sleepTalks: \(sleepTalks.count)")
        return sleepTalks.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        guard let cell = tableView.dequeueReusableCell(withIdentifier: "SleepTalkCell", for: indexPath) as? SleepTalkCell else {
            print("Unable to dequeue SleepTalkCell for row \(indexPath.row)")
            fatalError("Unable to dequeue SleepTalkCell")
        }
        
        let sleepTalk = sleepTalks[indexPath.row]
        cell.configure(with: sleepTalk)
        cell.playButton.tag = indexPath.row
        
        // Remove any existing targets to prevent multiple actions
        cell.playButton.removeTarget(nil, action: nil, for: .allEvents)
        cell.playButton.addTarget(self, action: #selector(playAudio), for: .touchUpInside)
        
        print("Configured cell for row \(indexPath.row) with phrase: \(sleepTalk.phrase)")
        
        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let sleepTalk = sleepTalks[indexPath.row]
        let detailVC = DetailViewController(sleepTalk: sleepTalk)
        let navController = UINavigationController(rootViewController: detailVC)
        present(navController, animated: true, completion: nil)
        tableView.deselectRow(at: indexPath, animated: true)
        print("Selected row \(indexPath.row) with phrase: \(sleepTalk.phrase)")
    }

    private func configureAudioSessionForPlayback() {
        do {
            try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
            try AVAudioSession.sharedInstance().setActive(true)
            print("Audio session configured for playback.")
        } catch {
            print("Failed to configure audio session for playback: \(error.localizedDescription)")
        }
    }
    
    
    @objc func playAudio(sender: UIButton) {
        let index = sender.tag
        guard index >= 0 && index < sleepTalks.count else {
            print("Invalid index \(index) for audio playback.")
            return
        }
        
        guard let audioURL = sleepTalks[index].audioURL else {
            print("No audio file URL available for row \(index)")
            return
        }
        
        print("Attempting to play audio file at URL: \(audioURL.path)")
        
        if FileManager.default.fileExists(atPath: audioURL.path) {
            do {
                // Stop any currently playing audio before starting a new one
                if let currentPlayer = audioPlayer, currentPlayer.isPlaying {
                    currentPlayer.stop()
                    print("Stopped currently playing audio.")
                }
                
                audioPlayer = try AVAudioPlayer(contentsOf: audioURL)
                audioPlayer?.delegate = self
                audioPlayer?.prepareToPlay()
                let isPlaying = audioPlayer?.play() ?? false
                
                if isPlaying {
                    print("Audio playback started successfully for file: \(audioURL.lastPathComponent)")
                } else {
                    print("Audio playback failed to start for file: \(audioURL.lastPathComponent)")
                }
            } catch {
                print("Failed to play audio: \(error.localizedDescription)")
            }
        } else {
            print("Audio file does not exist at path: \(audioURL.path)")
        }
    }
}
    
// MARK: - SFSpeechRecognizerDelegate

extension ViewController: SFSpeechRecognizerDelegate {
    func speechRecognizer(_ speechRecognizer: SFSpeechRecognizer, availabilityDidChange available: Bool) {
        startButton.isEnabled = available
        statusLabel.text = available ? "Speech recognizer is available" : "Speech recognizer is not available"
        if !available {
            print("Speech recognizer is currently unavailable.")
        }
    }

    func startSpeechRecognition(audioURL: URL) {
        if FileManager.default.fileExists(atPath: audioURL.path) {
            let recognitionRequest = SFSpeechURLRecognitionRequest(url: audioURL)
            speechRecognizer?.recognitionTask(with: recognitionRequest) { result, error in
                if let error = error {
                    print("Speech recognition failed: \(error.localizedDescription)")
                } else if let result = result {
                    print("Speech recognition result: \(result.bestTranscription.formattedString)")
                }
            }
        } else {
            print("Audio file does not exist at path: \(audioURL.path)")
        }
    }
}





// MARK: - AVAudioRecorderDelegate

extension ViewController: AVAudioRecorderDelegate {
    func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
        if !flag {
            stopRecording()
        }
    }
}

// MARK: - SleepTalkCell

class SleepTalkCell: UITableViewCell {
    private let phraseLabel = UILabel()
    private let dateLabel = UILabel()
    let playButton = UIButton(type: .system)
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupUI()
        print("SleepTalkCell initialized")
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // Inside SleepTalkCell class
    private func setupUI() {
        [phraseLabel, dateLabel, playButton].forEach {
            contentView.addSubview($0)
            $0.translatesAutoresizingMaskIntoConstraints = false
        }
        
        phraseLabel.numberOfLines = 0
        phraseLabel.font = UIFont.systemFont(ofSize: 16)
        
        dateLabel.font = UIFont.systemFont(ofSize: 12)
        dateLabel.textColor = .gray
        
        playButton.setTitle("Play", for: .normal)
        playButton.setTitleColor(.white, for: .normal)
        playButton.backgroundColor = .systemBlue
        playButton.layer.cornerRadius = 8
        playButton.clipsToBounds = true
        
        NSLayoutConstraint.activate([
            phraseLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
            phraseLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            phraseLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            
            dateLabel.topAnchor.constraint(equalTo: phraseLabel.bottomAnchor, constant: 8),
            dateLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            dateLabel.trailingAnchor.constraint(equalTo: playButton.leadingAnchor, constant: -8),
            dateLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
            
            playButton.centerYAnchor.constraint(equalTo: dateLabel.centerYAnchor),
            playButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            playButton.widthAnchor.constraint(equalToConstant: 60),
            playButton.heightAnchor.constraint(equalToConstant: 30)
        ])
    }

    func configure(with sleepTalk: SleepTalk) {
        phraseLabel.text = sleepTalk.phrase
        dateLabel.text = DateFormatter.localizedString(from: sleepTalk.date, dateStyle: .medium, timeStyle: .short)
        
        if let audioURL = sleepTalk.audioURL {
            if FileManager.default.fileExists(atPath: audioURL.path) {
                print("Audio file exists for SleepTalk ID: \(sleepTalk.id)")
            } else {
                print("Audio file is missing for SleepTalk ID: \(sleepTalk.id) at path: \(audioURL.path)")
            }
        } else {
            print("No audioURL for SleepTalk ID: \(sleepTalk.id)")
        }
    }
}

private func startButtonColorFluctuation(for button: UIButton, baseColor: UIColor) {
    let animation = CAKeyframeAnimation(keyPath: "backgroundColor")
    animation.values = [
        baseColor.cgColor,
        baseColor.withAlphaComponent(0.8).cgColor,
        baseColor.withAlphaComponent(0.6).cgColor,
        baseColor.withAlphaComponent(0.8).cgColor,
        baseColor.cgColor
    ]
    animation.keyTimes = [0, 0.25, 0.5, 0.75, 1]
    animation.duration = 2.0
    animation.repeatCount = .infinity
    button.layer.add(animation, forKey: "backgroundColorAnimation")
}



// MARK: - DetailViewController

class DetailViewController: UIViewController {
    var sleepTalk: SleepTalk
    
    private let scrollView = UIScrollView()
    private let contentView = UIView()
    
    private let phraseLabel = UILabel()
    private let interpretationLabel = UILabel()
    private let dateLabel = UILabel()
    private let playButton = UIButton(type: .system)
    private let deleteButton = UIButton(type: .system)
    private var audioPlayer: AVAudioPlayer?

    init(sleepTalk: SleepTalk) {
        self.sleepTalk = sleepTalk
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupUI()
        
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Close", style: .plain, target: self, action: #selector(dismissVC))
    }
    
    private func startHueFluctuation(for button: UIButton, baseColor: UIColor, hueColors: [UIColor]) {
        let smoothHueColors = hueColors + [hueColors.first!] // Ensure a complete cycle back to the start color

        // Create a CAKeyframeAnimation for the background color
        let animation = CAKeyframeAnimation(keyPath: "backgroundColor")
        animation.values = smoothHueColors.map { $0.cgColor }
        animation.keyTimes = Array(0..<smoothHueColors.count).map { NSNumber(value: Double($0) / Double(smoothHueColors.count - 1)) }
        
        animation.duration = 3.0 // Adjust for smoothness
        animation.repeatCount = .infinity
        button.layer.add(animation, forKey: "hueFluctuation")
    }
    
    // Inside DetailViewController class
    private func setupUI() {
        // Add ScrollView and ContentView
        view.addSubview(scrollView)
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(contentView)
        contentView.translatesAutoresizingMaskIntoConstraints = false
        
        // Add subviews to ContentView
        [phraseLabel, interpretationLabel, dateLabel, playButton, deleteButton].forEach {
            contentView.addSubview($0)
            $0.translatesAutoresizingMaskIntoConstraints = false
        }
        
        // Configure Labels and Buttons
        phraseLabel.text = "Phrase: \(sleepTalk.phrase)"
        phraseLabel.numberOfLines = 0

        updateInterpretationLabel()
        
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .medium
        dateFormatter.timeStyle = .short
        dateLabel.text = dateFormatter.string(from: sleepTalk.date)
        
        playButton.setTitle("Play Audio", for: .normal)
        playButton.setTitleColor(.white, for: .normal)
        playButton.backgroundColor = .systemBlue
        playButton.layer.cornerRadius = 10
        playButton.clipsToBounds = true
        playButton.addTarget(self, action: #selector(playButtonTapped), for: .touchUpInside)
        
        deleteButton.setTitle("Delete", for: .normal)
        deleteButton.setTitleColor(.white, for: .normal)
        deleteButton.backgroundColor = .systemRed
        deleteButton.layer.cornerRadius = 10
        deleteButton.clipsToBounds = true
        deleteButton.addTarget(self, action: #selector(deleteButtonTapped), for: .touchUpInside)
        
        // Layout Constraints
        NSLayoutConstraint.activate([
            // ScrollView constraints
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            
            // ContentView constraints
            contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
            contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
            
            // Phrase Label
            phraseLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
            phraseLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
            phraseLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
            
            // Interpretation Label
            interpretationLabel.topAnchor.constraint(equalTo: phraseLabel.bottomAnchor, constant: 20),
            interpretationLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
            interpretationLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
            
            // Date Label
            dateLabel.topAnchor.constraint(equalTo: interpretationLabel.bottomAnchor, constant: 20),
            dateLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
            dateLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
            
            // Play Button
            playButton.topAnchor.constraint(equalTo: dateLabel.bottomAnchor, constant: 20),
            playButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            playButton.widthAnchor.constraint(equalToConstant: 100),
            playButton.heightAnchor.constraint(equalToConstant: 50),
            
            // Delete Button
            deleteButton.topAnchor.constraint(equalTo: playButton.bottomAnchor, constant: 20),
            deleteButton.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            deleteButton.widthAnchor.constraint(equalToConstant: 100),
            deleteButton.heightAnchor.constraint(equalToConstant: 50),
            deleteButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20) // Ensures ContentView's bottom
        ])
        
        startHueFluctuation(for: playButton, baseColor: .systemBlue, hueColors: [.systemBlue, .systemCyan, .systemTeal, .systemBlue])
        startHueFluctuation(for: deleteButton, baseColor: .systemRed, hueColors: [.systemRed, .systemPink, .systemOrange, .systemRed])
    }
    
    private func updateInterpretationLabel() {
        // **UPDATED:** Removed "Interpretation:" prefix
        interpretationLabel.text = sleepTalk.interpretation ?? "Analyzing..."
        interpretationLabel.numberOfLines = 0
        
        if sleepTalk.interpretation == nil {
            if let viewController = presentingViewController as? ViewController {
                viewController.sendToLLM(text: sleepTalk.phrase, id: sleepTalk.id)
            }
        }
    }
    
    func updateInterpretation(id: UUID, interpretation: String) {
        if id == sleepTalk.id {
            sleepTalk.interpretation = interpretation
            updateInterpretationLabel()
        }
    }
    
    @objc private func dismissVC() {
        dismiss(animated: true, completion: nil)
    }
    
    @objc func playButtonTapped() {
        if let audioURL = sleepTalk.audioURL {
            print("Attempting to play audio from URL: \(audioURL)")
            
            if FileManager.default.fileExists(atPath: audioURL.path) {
                do {
                    try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
                    try AVAudioSession.sharedInstance().setActive(true)
                    
                    audioPlayer = try AVAudioPlayer(contentsOf: audioURL)
                    audioPlayer?.prepareToPlay()
                    audioPlayer?.play()
                    print("Audio playback started")
                } catch {
                    print("Failed to play audio: \(error.localizedDescription)")
                }
            } else {
                print("Audio file does not exist at path: \(audioURL.path)")
            }
        } else {
            print("No audio file available for this entry.")
        }
    }

    @objc func deleteButtonTapped() {
        let alert = UIAlertController(title: "Delete Phrase", message: "Are you sure you want to delete this phrase?", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { [weak self] _ in
            self?.deleteSleepTalk()
        }))
        present(alert, animated: true, completion: nil)
    }

    private func deleteSleepTalk() {
        if let viewController = presentingViewController as? ViewController {
            viewController.deleteSleepTalk(id: sleepTalk.id)
            dismiss(animated: true, completion: nil)
        }
    }
    
    private func startButtonColorFluctuation(for button: UIButton, baseColor: UIColor) {
        let animation = CAKeyframeAnimation(keyPath: "backgroundColor")
        animation.values = [
            baseColor.cgColor,
            baseColor.withAlphaComponent(0.8).cgColor,
            baseColor.withAlphaComponent(0.6).cgColor,
            baseColor.withAlphaComponent(0.8).cgColor,
            baseColor.cgColor
        ]
        animation.keyTimes = [0, 0.25, 0.5, 0.75, 1]
        animation.duration = 2.0
        animation.repeatCount = .infinity
        button.layer.add(animation, forKey: "backgroundColorAnimation")
    }
}

// MARK: - SummaryViewController

class SummaryViewController: UIViewController {
    private let summaryTextView = UITextView()
    private let summary: String

    init(summary: String) {
        self.summary = summary
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupUI()

        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Close", style: .plain, target: self, action: #selector(dismissVC))
    }

    private func setupUI() {
        view.addSubview(summaryTextView)
        summaryTextView.translatesAutoresizingMaskIntoConstraints = false
        summaryTextView.isEditable = false
        summaryTextView.font = UIFont.systemFont(ofSize: 16)
        summaryTextView.text = summary

        NSLayoutConstraint.activate([
            summaryTextView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
            summaryTextView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
            summaryTextView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            summaryTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20)
        ])
    }

    @objc private func dismissVC() {
        dismiss(animated: true, completion: nil)
    }
}