import SwiftUI
import FoundationModels
import Observation
import SwiftData

@Observable class ApplicationData {
   var response: AttributedString = ""
   var prompt: String = ""
   
   var session = LanguageModelSession()
   var conversation: Conversation = Conversation(title: "")

   static let shared: ApplicationData = ApplicationData()
   private init() { }

   func sendPrompt(dbContext: ModelContext) async {
      do {
         let answer = try await session.respond(to: prompt)
         var newResponse = AttributedString("\(answer.content)\n\n")
         newResponse.font = .system(size: 16, weight: .regular)
         response.append(newResponse)
         
         // Update transcript
         conversation.transcript = try JSONEncoder().encode(session.transcript)
         try dbContext.save()
      } catch {
         response = AttributedString("Error accessing the model: \(error)")
      }
      prompt = ""
   }
   func loadConversation(dbContext: ModelContext, conversationID: UUID) {
      do {
         response = AttributedString("")
         prompt = ""

         // Get conversation
         let predicate = #Predicate<Conversation> { conversation in
            conversation.id == conversationID
         }
         let descriptor = FetchDescriptor<Conversation>(predicate: predicate)
         if let oldConversation = try? dbContext.fetch(descriptor).first {
            conversation = oldConversation
         } else {
            return
         }

         // Create session with old transcript
         if let data = conversation.transcript {
            let transcript = try JSONDecoder().decode(Transcript.self, from: data)
            session = LanguageModelSession(transcript: transcript)
            session.prewarm()

            // Add entries to the conversation
            for entry in transcript {
               switch entry {
               case .prompt(let prompt):
                  if let text = prompt.segments.first?.description {
                     var newText = AttributedString("\(text)\n\n")
                     newText.font = .system(size: 16, weight: .bold)
                     self.response.append(newText)
                  }
               case .response(let response):
                  if let text = response.segments.first?.description {
                     var newText = AttributedString("\(text)\n\n")
                     newText.font = .system(size: 16, weight: .regular)
                     self.response.append(newText)
                  }
               default:
                  break
               }
            }
         } else {
            // Create empty session
            session = LanguageModelSession()
         }
      } catch {
         print("Error decoding data")
      }
   }
   func createConversation(dbContext: ModelContext) {
      // Create an empty session
      response = AttributedString("")
      prompt = ""
      session = LanguageModelSession()

      // Create new conversation
      let newTitle = "New Conversation \(Int.random(in: 0...1000))"
      let newConversation = Conversation(title: newTitle)
      dbContext.insert(newConversation)
      try? dbContext.save()

      conversation = newConversation
   }
}