# app/services/analytics/planilha_entregas_xlsx.rb
#
# Gera o binário .xlsx da planilha Entregas do cliente com as 3 abas do modelo
# "Entregas SUDESTE MM.AAAA_FINAL.xlsx": RESUMO, ENTREGAS e SimpliRoute.
# Os dados vêm de Analytics::PlanilhaEntregas.
#
# As cores/estilos foram extraídos do próprio arquivo modelo (styles.xml):
# ENTREGAS A..T azul 1155CC e U..Z vermelho C00000 (texto branco, negrito);
# RESUMO com quadros em 002060/0070C0/C00000/A5A5A5; SimpliRoute sem estilo.
# (Os gráficos embutidos do modelo não são replicados.)
require 'caxlsx'

module Analytics
  class PlanilhaEntregasXlsx
    def initialize(planilha)
      @planilha = planilha
    end

    # String binária do .xlsx.
    def gerar
      pkg = Axlsx::Package.new
      wb  = pkg.workbook
      definir_estilos(wb)

      aba_resumo(wb)
      aba_entregas(wb)
      aba_simpliroute(wb)

      pkg.to_stream.read
    end

    private

    def definir_estilos(wb)
      st     = wb.styles
      borda  = { style: :thin, color: 'FF808080', edges: %i[left right top bottom] }
      centro = { horizontal: :center, vertical: :center }

      # Cabeçalhos/civis do RESUMO
      @titulo_mes = st.add_style(b: true)
      @titulo_sec = st.add_style(b: true, sz: 16, u: true)
      @rotulo_kpi = st.add_style(b: true, sz: 10)
      @valor_kpi  = st.add_style(b: true, sz: 10, border: { style: :thin, color: 'FF000000', edges: [:bottom] })
      @valor_pct  = st.add_style(b: true, sz: 10, format_code: '0.0%',
                                 border: { style: :thin, color: 'FF000000', edges: [:bottom] })

      @h_navy   = st.add_style(bg_color: '002060', fg_color: 'FFFFFF', b: true, sz: 11, border: borda, alignment: centro)
      @h_azul   = st.add_style(bg_color: '0070C0', fg_color: 'FFFFFF', b: true, sz: 11, border: borda, alignment: centro)
      @h_verm   = st.add_style(bg_color: 'C00000', fg_color: 'FFFFFF', b: true, sz: 11, border: borda, alignment: centro)
      @h_cinza  = st.add_style(bg_color: 'A5A5A5', fg_color: 'FFFFFF', sz: 10, border: borda,
                               alignment: centro.merge(wrap_text: true))
      @h_motivo = st.add_style(b: true, sz: 10, border: borda, alignment: centro.merge(wrap_text: true))

      @celula  = st.add_style(sz: 11, border: borda)
      @cel_num = st.add_style(sz: 11, border: borda, alignment: centro)

      # Status das Entregas / Conferência Documentos (verde "bom", laranja "em
      # andamento", azul "a iniciar" — como no modelo)
      @st_verde    = st.add_style(bg_color: 'C6EFCE', fg_color: '006100', b: true, sz: 10, border: borda, alignment: centro)
      @st_laranja  = st.add_style(bg_color: 'FCE4D6', fg_color: 'ED7D31', sz: 10, border: borda, alignment: centro)
      @st_iniciar  = st.add_style(fg_color: '0070C0', sz: 10, border: borda, alignment: centro)

      @perf_rotulo = st.add_style(i: true)
      @perf_pct    = st.add_style(i: true, format_code: '0.0%')

      # Cabeçalho da ENTREGAS
      @cab_azul = st.add_style(bg_color: '1155CC', fg_color: 'FFFFFF', b: true)
      @cab_verm = st.add_style(bg_color: 'C00000', fg_color: 'FFFFFF', b: true)

      # Hiperlink (fotos/comprovantes do SimpliRoute) — azul sublinhado padrão.
      @link = st.add_style(fg_color: '0563C1', u: true)
    end

    # ── RESUMO ───────────────────────────────────────────────────
    # Mesma disposição do modelo: mês, totais/performance e um quadro por grupo
    # (EMAD e UBS) com as coordenadorias fixas + motivos de "Não Entregue".
    # ("Cordenadoria" sem o primeiro "o" é verbatim do modelo.)
    def aba_resumo(wb)
      resumo = @planilha.resumo
      wb.add_worksheet(name: 'RESUMO') do |s|
        s.add_row []
        s.add_row ['', resumo[:mes]], style: [nil, @titulo_mes]
        s.add_row []
        s.add_row ['', 'Total Previsto:',  resumo[:total_previsto]],  style: [nil, @rotulo_kpi, @valor_kpi]
        s.add_row ['', 'Total Realizado:', resumo[:total_realizado]], style: [nil, @rotulo_kpi, @valor_kpi]
        s.add_row ['', 'Performance:',     resumo[:performance] || ''],
                  style: [nil, @rotulo_kpi, (resumo[:performance] ? @valor_pct : @valor_kpi)]

        PlanilhaEntregas::GRUPOS.each { |grupo| quadro_grupo(s, grupo, resumo[:grupos][grupo]) }

        s.column_widths 3, 18, 10, 10, 12, 14, 14, 3, 10, 12, 14, 10, 14, 10
      end
    end

    def quadro_grupo(s, grupo, coords)
      s.add_row []
      s.add_row ['', grupo], style: [nil, @titulo_sec]

      # "Informações" (F:G) e 'Motivos de "Não Entregue"' (I:N) mescladas
      s.add_row ['', '', '', '', '', 'Informações', '', '', 'Motivos de "Não Entregue"', '', '', '', '', ''],
                style: [nil, nil, nil, nil, nil, @h_motivo, @h_motivo, nil, *Array.new(6, @h_motivo)]
      r = s.rows.size
      s.merge_cells("F#{r}:G#{r}")
      s.merge_cells("I#{r}:N#{r}")

      s.add_row ['', 'Cordenadoria', 'Previsão', 'Entregue', 'Não Entregue',
                 'Status das Entregas', 'Conferência Documentos', '',
                 *PlanilhaEntregas::MOTIVOS],
                style: [nil, @h_navy, @h_azul, @h_navy, @h_verm, @h_cinza, @h_cinza, nil,
                        *Array.new(PlanilhaEntregas::MOTIVOS.size, @h_motivo)]

      # Coordenadorias fixas do modelo + eventuais extras vindas dos dados.
      nomes = PlanilhaEntregas::COORDENADORIAS + (coords.keys - PlanilhaEntregas::COORDENADORIAS)
      total = @planilha.novo_bloco

      nomes.each do |nome|
        b = coords[nome] || @planilha.novo_bloco
        total[:previsao]     += b[:previsao]
        total[:entregue]     += b[:entregue]
        total[:nao_entregue] += b[:nao_entregue]
        total[:pendente]     += b[:pendente]
        b[:motivos].each { |m, q| total[:motivos][m] += q }

        s.add_row ['', nome, b[:previsao], b[:entregue], b[:nao_entregue],
                   (b[:pendente].zero? ? 'Concluído' : 'Em andamento'), 'A iniciar', '',
                   *PlanilhaEntregas::MOTIVOS.map { |m| b[:motivos][m] }],
                  style: [nil, @celula, @cel_num, @cel_num, @cel_num,
                          (b[:pendente].zero? ? @st_verde : @st_laranja), @st_iniciar, nil,
                          *Array.new(PlanilhaEntregas::MOTIVOS.size, @cel_num)]
      end

      s.add_row ['', 'Total', total[:previsao], total[:entregue], total[:nao_entregue], '', '', '',
                 *PlanilhaEntregas::MOTIVOS.map { |m| total[:motivos][m] }],
                style: [nil, @h_navy, @h_azul, @h_navy, @h_verm, nil, nil, nil,
                        *Array.new(PlanilhaEntregas::MOTIVOS.size, @h_motivo)]

      if total[:previsao].positive?
        s.add_row ['', 'Performance %', 1,
                   total[:entregue].to_f / total[:previsao],
                   total[:nao_entregue].to_f / total[:previsao]],
                  style: [nil, @perf_rotulo, @perf_pct, @perf_pct, @perf_pct]
      else
        s.add_row ['', 'Performance %', '', '', ''], style: [nil, @perf_rotulo]
      end
    end

    # ── ENTREGAS ─────────────────────────────────────────────────
    # A..V direto da tabela gade (texto); W..Z do rastreio:
    # W STATUS (completed/failed...), X ENTREGA (Sim/Não), Y DATA OCORRÊNCIA
    # (data real do checkout), Z OCORRÊNCIA (motivo do insucesso).
    # Cabeçalho como no modelo: A..T azul, U..Z (resultado) vermelho.
    def aba_entregas(wb)
      linhas = @planilha.linhas
      azuis  = PlanilhaEntregas::COLUNAS_GADE.size - 2   # A..T
      wb.add_worksheet(name: 'ENTREGAS') do |sheet|
        sheet.add_row(PlanilhaEntregas::CABECALHO,
                      style: Array.new(azuis, @cab_azul) +
                             Array.new(PlanilhaEntregas::CABECALHO.size - azuis, @cab_verm))
        linhas.each do |l|
          valores, tipos = linha_entrega(l)
          sheet.add_row(valores, types: tipos)
        end
        sheet.auto_filter = "A1:Z#{linhas.size + 1}"
      end
    end

    def linha_entrega(l)
      status_r = l['rastreio_status'].to_s
      entregue = if status_r == 'completed'
                   'Sim'
                 elsif Entrega::STATUS_FALHA.include?(status_r)
                   'Não'
                 else
                   ''
                 end
      data_ocorrencia = data_de(l['rastreio_checkout'])

      valores = PlanilhaEntregas::COLUNAS_GADE.keys.map { |c| l[c].to_s } +
                [status_r, entregue, data_ocorrencia || '', l['rastreio_observation'].to_s]
      tipos = Array.new(PlanilhaEntregas::COLUNAS_GADE.size + 2, :string) +
              [(data_ocorrencia ? :date : :string), :string]
      [valores, tipos]
    end

    # ── SimpliRoute ──────────────────────────────────────────────
    # Dump cru do rastreio (todas as visitas das NFs da operação), com o
    # cabeçalho exato do modelo — sem estilo, como no original; cabeçalho sem
    # coluna correspondente sai vazio.
    #
    # As colunas de foto (Nota Fiscal, Termo de Recebimento, Foto da Fachada,
    # Relatório de visita) guardam URLs do S3 do SimpliRoute: viram HIPERLINK
    # clicável (azul sublinhado), para o cliente abrir o comprovante direto do
    # Excel em vez de copiar e colar a URL.
    def aba_simpliroute(wb)
      colunas = @planilha.colunas_rastreio
      linhas  = @planilha.linhas_rastreio
      wb.add_worksheet(name: 'SimpliRoute') do |sheet|
        sheet.add_row(colunas.keys)
        linhas.each do |r|
          valores = colunas.values.map { |col| col ? texto(r[col]) : '' }
          sheet.add_row(valores, types: Array.new(colunas.size, :string))
          linkar_urls(sheet, valores)
        end
        sheet.auto_filter = "A1:#{Axlsx.col_ref(colunas.size - 1)}#{linhas.size + 1}"
      end
    end

    # Transforma em hiperlink toda célula da linha recém-adicionada cujo valor
    # seja uma URL http(s) — mesma checagem do foto_url do OperacaoMetricas,
    # para não jogar lixo dentro do <hyperlink> do arquivo.
    def linkar_urls(sheet, valores)
      linha = sheet.rows.size
      valores.each_with_index do |valor, i|
        next unless valor.to_s.match?(%r{\Ahttps?://\S+\z}i)

        sheet.add_hyperlink(location: valor, ref: "#{Axlsx.col_ref(i)}#{linha}")
        sheet.rows.last.cells[i].style = @link
      end
    end

    def texto(valor)
      valor.respond_to?(:strftime) ? valor.strftime('%d/%m/%Y %H:%M') : valor.to_s
    end

    def data_de(valor)
      return nil if valor.nil?
      return valor.to_date if valor.respond_to?(:to_date)
      Date.parse(valor.to_s)
    rescue ArgumentError, TypeError
      nil
    end
  end
end
