module CrmInsights
  class GenerateService < Llm::BaseAiService
    DEFAULT_MODEL = 'gpt-4o-mini'

    def initialize(conversation:, insight:, sessions_count:, last_contact_at:, from_message_id: nil, to_message_id: nil)
      super()
      @conversation = conversation
      @insight = insight
      @sessions_count = sessions_count
      @last_contact_at = last_contact_at
      @from_message_id = from_message_id
      @to_message_id = to_message_id
      @model = ENV.fetch('CRM_INSIGHTS_MODEL', DEFAULT_MODEL)
    end

    def generate
      chat = RubyLLM.chat(model: @model)
                    .with_temperature(0.2)
                    .with_params(response_format: { type: 'json_object' })
      response = chat.ask(prompt)
      parsed = parse_response(response)
      return { data: nil, error: 'Resposta invalida do modelo' } if parsed.blank?

      { data: parsed, error: nil }
    rescue StandardError => e
      Rails.logger.error "[CRM Insights] Generation failed: #{e.message}"
      { data: nil, error: e.message }
    end

    private

    def prompt
      <<~PROMPT
        Voce eh uma IA de CRM inteligente para atendimento. Gere um perfil vivo do cliente.

        Regras:
        - Idioma: PT-BR sempre.
        - Nao resuma a conversa; gere um perfil do cliente.
        - Frases curtas, estilo CRM humano.
        - Sem listas longas. Use bullets curtos apenas nos blocos de padroes e friccoes.
        - Atualize o resumo existente sem perder informacoes relevantes.
        - Priorize padroes recorrentes sobre eventos isolados.
        - Se dados forem insuficientes, diga que faltam sinais claros.
        - So inclua frictions e contact_pattern se houver evidencia explicita no historico abaixo.
        - Nao preencha valores padrao. Se nao houver sinal, use lista vazia ou campo vazio.
        - Nunca invente horarios ou dias. Se nao houver mencao direta, deixe contact_pattern vazio.
        - Nunca invente friccoes. Se nao houver mencao direta, deixe frictions vazio.
        - Se houver menos de 3 mensagens do cliente no historico, gere um resumo minimalista apenas com fatos explicitos.

        Saida OBRIGATORIA (JSON valido):
        {
          "summary_text": "texto humano completo para UI",
          "structured_data": {
            "summary_text": "...",
            "preferences": [],
            "contact_pattern": { "time_range": "", "days": [] },
            "intent": "",
            "price_sensitivity": "",
            "urgency": "",
            "frictions": [],
            "commercial_status": "",
            "customer_potential": "",
            "agent_tip": "",
            "funnel": {
              "stage": "info", // enum: info, price, availability, confirmation, closed_won, closed_lost
              "confidence": 0.0, // float 0-1
              "reason": "justificativa curta",
              "evidence_message_ids": [], // IDs das mensagens que justificam o estagio
              "updated_at": "ISO8601" // data atual se houve mudanca, ou manter anterior
            }
          }
        }

        REGRAS FUNIL DE VENDAS (CRITICO):
        1. Analise APENAS o historico fornecido abaixo para definir o estagio.
        2. Estagios:
           - info: pede informacoes gerais. (Confianca minima: qualquer)
           - price: discute valores. (Confianca minima: 0.6)
           - availability: pergunta sobre datas/vagas. (Confianca minima: 0.6)
           - confirmation: sinaliza reserva/pagamento. (Confianca minima: 0.75)
           - closed_won: confirmou reserva explicitamente ("ja paguei", "reservado"). (Confianca minima: 0.85)
           - closed_lost: desistiu explicitamente ("nao vou querer", "fica pra proxima"). (Confianca minima: 0.85)
        3. Se nao houver mensagens NOVAS suficientes para mudar de estagio com confianca, mantenha o estagio anterior (se fornecido no JSON anterior) ou retorne "info" se for o inicio.
        4. NUNCA avance para closed_won/lost sem evidencia explicita de fechamento ou perda.
        5. "evidence_message_ids" eh OBRIGATORIO. Se estiver vazio, o estagio deve ser considerado invalido ou "info".

        Contexto:
        - Canal: #{channel_name}
        - Conversa ID: #{@conversation.id}
        - Contatos (24h): #{@sessions_count}
        - Ultimo contato valido: #{format_time(@last_contact_at)}
        - Intervalo de mensagens: #{message_range_label}

        Resumo anterior (se existir):
        #{@insight&.summary_text || 'Sem resumo anterior.'}

        JSON anterior (se existir):
        #{(@insight&.structured_data || {}).to_json}

        Historico recente (ate 50 mensagens):
        #{history_block}

        Formato do texto humano (exemplo de estilo):
        Cliente recorrente.
        Demonstra preferencia por suites com hidro.
        Costuma entrar em contato a noite (principalmente entre 19h e 23h).
        Ja perguntou diversas vezes sobre formas de pagamento e horarios de check-in.
        Perfil objetivo, poucas mensagens.

        Intencao predominante: reserva rapida
        Sensibilidade a preco: media
        Urgencia: alta

        Padrao de contato:
        • Horario: entre 19h e 23h
        • Dias mais comuns: sexta e sabado

        Pontos de atencao:
        • Duvidas recorrentes sobre formas de pagamento
        • Questionamentos frequentes sobre horario de check-in

        Status comercial atual: 🟢 Alta chance de conversao (Estagio: Disponibilidade)

        Potencial do cliente:
        • Perfil recorrente
        • Compativel com suites premium
        • Bom candidato a fidelizacao

        Dica para atendimento: seja direto, informe valor e disponibilidade rapidamente e foque em suites com hidro.
      PROMPT
    end

    def history_block
      messages = @conversation.messages
                              .where(message_type: %i[incoming outgoing], private: false)
      messages = messages.where('id >= ?', @from_message_id) if @from_message_id
      messages = messages.where('id <= ?', @to_message_id) if @to_message_id
      messages = messages.order(created_at: :desc).limit(50).reverse
      messages.map do |message|
        role = message.incoming? ? 'Cliente' : 'Atendente'
        time = message.created_at&.strftime('%d/%m/%Y %H:%M')
        "#{time} - #{role}: #{message.content}"
      end.join("\n")
    end

    def channel_name
      @conversation.inbox&.channel_type.to_s
    end

    def format_time(value)
      return 'Desconhecido' if value.blank?

      value.strftime('%d/%m/%Y %H:%M')
    end

    def parse_response(response)
      content = response.respond_to?(:content) ? response.content : response.to_s
      JSON.parse(content)
    rescue JSON::ParserError => e
      Rails.logger.error "[CRM Insights] JSON parse failed: #{e.message}"
      nil
    end

    def message_range_label
      return 'Completo (ate 50 mensagens)' if @from_message_id.blank? && @to_message_id.blank?
      return "A partir de #{@from_message_id}" if @to_message_id.blank?
      return "Ate #{@to_message_id}" if @from_message_id.blank?

      "#{@from_message_id} ate #{@to_message_id}"
    end
  end
end
