/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, Subject } from 'rxjs';

import { Message } from '../pojo/Message';
import { LocalStorageService } from './local-storage.service';

export interface ChatMessage {
  content: string;
  role: 'user' | 'assistant' | 'system_push';
  gmtCreate: Date;
  securityForm: SecurityForm;
  /** Flag indicating this message is a skill report that should be displayed directly */
  isSkillReport?: boolean;
}

export interface SecurityForm {
  show: Boolean;
  param: string;
  content: string;
  complete: boolean;
}

export const DEFAULT_SECURITY_FORM: SecurityForm = {
  show: false,
  param: '',
  content: '',
  complete: false
};

export interface ChatConversation {
  id: number;
  title: string;
  gmtCreated: Date;
  gmtUpdate: Date;
  messages: ChatMessage[];
}

export interface ChatRequestContext {
  message: string;
  conversationId?: number;
}

export interface SopSchedule {
  id?: number;
  conversationId: number;
  sopName: string;
  sopParams?: string;
  cronExpression: string;
  enabled: boolean;
  lastRunTime?: Date;
  nextRunTime?: Date;
  gmtCreate?: Date;
  gmtUpdate?: Date;
}

export interface SkillInfo {
  name: string;
  description: string;
}

const chat_uri = '/chat';
const schedule_uri = '/ai/schedule';

@Injectable({
  providedIn: 'root'
})
export class AiChatService {
  constructor(private http: HttpClient, private localStorageService: LocalStorageService) {}

  /**
   * Create a new conversation
   */
  createConversation(): Observable<Message<ChatConversation>> {
    return this.http.post<Message<ChatConversation>>(`${chat_uri}/conversations`, {});
  }

  /**
   * Get all conversations
   */
  getConversations(): Observable<Message<ChatConversation[]>> {
    return this.http.get<Message<ChatConversation[]>>(`${chat_uri}/conversations`);
  }

  /**
   * Get a specific conversation with its history
   */
  getConversation(conversationId: number): Observable<Message<ChatConversation>> {
    return this.http.get<Message<ChatConversation>>(`${chat_uri}/conversations/${conversationId}`);
  }

  /**
   * Delete a conversation
   */
  deleteConversation(conversationId: number): Observable<Message<void>> {
    return this.http.delete<Message<void>>(`${chat_uri}/conversations/${conversationId}`);
  }

  /**
   * Send a message and get streaming response
   */
  streamChat(message: string, conversationId?: number): Observable<ChatMessage> {
    const responseSubject = new Subject<ChatMessage>();

    const requestBody: ChatRequestContext = { message };
    if (conversationId) {
      requestBody.conversationId = conversationId;
    }

    const token = this.localStorageService.getAuthorizationToken();
    const headers: Record<string, string> = {
      'Content-Type': 'application/json',
      Accept: 'text/event-stream',
      'Cache-Control': 'no-cache'
    };

    // Add Authorization header like the interceptor does
    if (token) {
      headers['Authorization'] = `Bearer ${token}`;
    }

    // Use fetch for SSE streaming (HttpClient doesn't support true streaming)
    fetch(`/api${chat_uri}/stream`, {
      method: 'POST',
      headers,
      body: JSON.stringify(requestBody)
    })
      .then(response => {
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }

        const reader = response.body?.getReader();
        if (!reader) {
          throw new Error('No reader available');
        }

        const decoder = new TextDecoder();
        let buffer = '';

        const readStream = (): Promise<void> => {
          if (!reader) {
            return Promise.resolve();
          }
          return reader.read().then(({ value, done }) => {
            if (done) {
              responseSubject.complete();
              return;
            }

            const chunk = decoder.decode(value, { stream: true });

            buffer += chunk;
            const lines = buffer.split('\n');
            buffer = lines.pop() || '';

            for (const line of lines) {
              const trimmedLine = line.trim();
              if (trimmedLine.startsWith('data:')) {
                const jsonStr = trimmedLine.substring(5).trim();
                if (jsonStr && jsonStr !== '[DONE]') {
                  try {
                    const data = JSON.parse(jsonStr);
                    if (data.response !== undefined) {
                      responseSubject.next({
                        content: data.response || '',
                        role: 'assistant',
                        securityForm: DEFAULT_SECURITY_FORM,
                        gmtCreate: data.timestamp ? new Date(data.timestamp) : new Date()
                      });
                    }
                  } catch (parseError) {
                    console.error('Error parsing SSE data:', parseError, 'Raw data:', jsonStr);
                    if (jsonStr) {
                      responseSubject.next({
                        content: jsonStr,
                        role: 'assistant',
                        securityForm: DEFAULT_SECURITY_FORM,
                        gmtCreate: new Date()
                      });
                    }
                  }
                }
              }
            }

            return readStream();
          });
        };

        return readStream();
      })
      .catch(error => {
        console.error('Chat stream error:', error);
        responseSubject.error(error);
      });

    return responseSubject.asObservable();
  }

  saveSecurityData(body: any): Observable<Message<any>> {
    return this.http.post<Message<any>>(`${chat_uri}/security`, body);
  }

  // ===== Schedule API Methods =====

  /**
   * Get all schedules for a conversation
   */
  getSchedules(conversationId: number): Observable<Message<SopSchedule[]>> {
    return this.http.get<Message<SopSchedule[]>>(`${schedule_uri}/conversation/${conversationId}`);
  }

  /**
   * Create a new schedule
   */
  createSchedule(schedule: SopSchedule): Observable<Message<SopSchedule>> {
    return this.http.post<Message<SopSchedule>>(schedule_uri, schedule);
  }

  /**
   * Update a schedule
   */
  updateSchedule(id: number, schedule: SopSchedule): Observable<Message<SopSchedule>> {
    return this.http.put<Message<SopSchedule>>(`${schedule_uri}/${id}`, schedule);
  }

  /**
   * Delete a schedule
   */
  deleteSchedule(id: number): Observable<Message<void>> {
    return this.http.delete<Message<void>>(`${schedule_uri}/${id}`);
  }

  /**
   * Toggle schedule enabled status
   */
  toggleSchedule(id: number, enabled: boolean): Observable<Message<SopSchedule>> {
    return this.http.put<Message<SopSchedule>>(`${schedule_uri}/${id}/toggle?enabled=${enabled}`, {});
  }

  /**
   * Get available SOP skills
   */
  getAvailableSkills(): Observable<Message<SkillInfo[]>> {
    return this.http.get<Message<SkillInfo[]>>(`${schedule_uri}/skills`);
  }
}
