// Check-in com GPS + foto obrigatoria — orcamento 298, Fase 1.
//
// Ate aqui o check-in registrava so a HORA (migration 026): a promotora podia
// dar check-in de casa. Este arquivo fecha isso — localizacao + foto tirada na
// hora, com o raio validado no banco (migration 038).
//
// A validacao que VALE e a do Postgres. O que esta aqui e para a promotora ver
// o problema antes de gastar dados enviando a foto, e para trocar o erro cru do
// navegador por uma frase em portugues.
//
// Sequencia curta de proposito (secao "Diretriz principal" da especificacao):
//   consentimento LGPD (so na primeira vez) -> localizar -> foto -> pronto.
// Nenhum campo de texto obrigatorio: cada campo a mais derruba a adesao da
// equipe em campo.

const GEO_TIMEOUT_MS = 20000;
const GEO_PRECISAO_RUIM_M = 100; // acima disso o aparelho usa torre/wifi, nao GPS

const GEO_ERROS = {
  1: 'Você precisa permitir o acesso à localização para fazer o check-in. Abra os ajustes do navegador e libere a localização para este site.',
  2: 'Não foi possível obter sua localização. Verifique se o GPS do aparelho está ligado.',
  3: 'A localização demorou demais para responder. Vá para um lugar mais aberto e tente de novo.',
};

function obterPosicao() {
  return new Promise((resolve, reject) => {
    if (!navigator.geolocation) {
      reject(new Error('Este aparelho não permite acesso à localização.'));
      return;
    }
    navigator.geolocation.getCurrentPosition(
      pos => resolve({
        lat: pos.coords.latitude,
        lng: pos.coords.longitude,
        precisao: pos.coords.accuracy,
      }),
      err => reject(new Error(GEO_ERROS[err.code] || 'Não foi possível obter sua localização.')),
      { enableHighAccuracy: true, timeout: GEO_TIMEOUT_MS, maximumAge: 0 },
    );
  });
}

// Mesma formula da funcao distancia_metros do banco (Haversine).
function distanciaMetros(lat1, lng1, lat2, lng2) {
  const vals = [lat1, lng1, lat2, lng2];
  if (vals.some(v => v === null || v === undefined || v === '')) return null;
  const rad = g => (Number(g) * Math.PI) / 180;
  const dLat = rad(lat2) - rad(lat1);
  const dLng = rad(lng2) - rad(lng1);
  const a = Math.pow(Math.sin(dLat / 2), 2)
    + Math.cos(rad(lat1)) * Math.cos(rad(lat2)) * Math.pow(Math.sin(dLng / 2), 2);
  return Math.round(6371000 * 2 * Math.asin(Math.sqrt(a)));
}

// O endereco por extenso e um CONFORTO, nao um requisito: se o servico estiver
// fora do ar o check-in continua valendo pelas coordenadas. Por isso engole o
// erro e devolve null.
async function enderecoAproximado(lat, lng) {
  try {
    const ctrl = new AbortController();
    const timer = setTimeout(() => ctrl.abort(), 6000);
    const resp = await fetch(
      'https://nominatim.openstreetmap.org/reverse?format=jsonv2&zoom=18&accept-language=pt-BR&lat=' + lat + '&lon=' + lng,
      { signal: ctrl.signal, headers: { Accept: 'application/json' } },
    );
    clearTimeout(timer);
    if (!resp.ok) return null;
    const json = await resp.json();
    return (json && json.display_name) || null;
  } catch (err) {
    console.warn('[enderecoAproximado] indisponivel:', err && err.message);
    return null;
  }
}

const fmtDistancia = m => (m === null || m === undefined)
  ? null
  : (m < 1000 ? Math.round(m) + ' m' : (m / 1000).toFixed(1).replace('.', ',') + ' km');

function CheckInModal({ open, escala, freelancer, onClose, onDone, showToast }) {
  const ev = (escala && escala.events) || {};
  const precisaLgpd = !(freelancer && freelancer.lgpd_localizacao_aceita_em);
  const raio = ev.raio_checkin_metros || 300;

  const [etapa, setEtapa] = React.useState('localizando'); // lgpd|localizando|erro|foto|enviando
  const [pos, setPos] = React.useState(null);
  const [distancia, setDistancia] = React.useState(null);
  const [endereco, setEndereco] = React.useState(null);
  const [erro, setErro] = React.useState('');
  const [arquivo, setArquivo] = React.useState(null);
  const [preview, setPreview] = React.useState('');

  // URL de preview e um recurso do navegador: sem o revoke, cada foto tirada
  // deixa um blob preso na memoria do celular.
  React.useEffect(() => () => { if (preview) URL.revokeObjectURL(preview); }, [preview]);

  const localizar = React.useCallback(async () => {
    setEtapa('localizando');
    setErro('');
    try {
      const p = await obterPosicao();
      setPos(p);

      const dist = distanciaMetros(ev.local_lat, ev.local_lng, p.lat, p.lng);
      setDistancia(dist);

      if (dist !== null && dist > raio) {
        setErro('Você está a ' + fmtDistancia(dist) + ' do local da ação (limite: '
          + raio + ' m). Aproxime-se do endereço informado para fazer o check-in.');
        setEtapa('erro');
        return;
      }

      setEtapa('foto');
      enderecoAproximado(p.lat, p.lng).then(setEndereco); // segue sem esperar
    } catch (err) {
      setErro(err.message);
      setEtapa('erro');
    }
  }, [ev.local_lat, ev.local_lng, raio]);

  React.useEffect(() => {
    if (!open) return;
    setArquivo(null);
    setPreview('');
    if (precisaLgpd) setEtapa('lgpd');
    else localizar();
  }, [open, precisaLgpd, localizar]);

  const aceitarLgpd = async () => {
    const { error } = await sb.rpc('promotor_aceitar_lgpd');
    if (error) {
      console.error('[CheckInModal.aceitarLgpd] Supabase error:', error);
      showToast('Não foi possível registrar o aceite.', 'danger');
      return;
    }
    if (freelancer) freelancer.lgpd_localizacao_aceita_em = new Date().toISOString();
    localizar();
  };

  const escolherFoto = (file) => {
    if (!file) return;
    if (preview) URL.revokeObjectURL(preview);
    setArquivo(file);
    setPreview(URL.createObjectURL(file));
  };

  const confirmar = async () => {
    if (!arquivo || !pos) return;
    setEtapa('enviando');
    const ext = (arquivo.name.split('.').pop() || 'jpg').toLowerCase();
    const path = 'checkin_' + escala.id + '/' + crypto.randomUUID() + '.' + ext;
    try {
      const { error: upErr } = await sb.storage
        .from('event-photos').upload(path, arquivo, { contentType: arquivo.type });
      if (upErr) throw upErr;

      const { data: pub } = sb.storage.from('event-photos').getPublicUrl(path);

      const { data, error } = await sb.rpc('promotor_check_in', {
        p_escala_id: escala.id,
        p_lat: pos.lat,
        p_lng: pos.lng,
        p_foto_url: pub.publicUrl,
        p_foto_path: path,
        p_precisao_m: pos.precisao,
        p_endereco: endereco,
      });
      if (error) {
        // A foto so existe por causa deste check-in: se ele nao entrou, ela vira lixo.
        await sb.storage.from('event-photos').remove([path]);
        throw error;
      }

      onDone(Array.isArray(data) ? data[0] : data);
      showToast('Check-in realizado com sucesso!');
      onClose();
    } catch (err) {
      console.error('[CheckInModal.confirmar] error:', err);
      setErro(err.message || 'Não foi possível concluir o check-in.');
      setEtapa('erro');
    }
  };

  return (
    <Modal open={open} onClose={etapa === 'enviando' ? undefined : onClose} title="Check-in" width={420}>
      {etapa === 'lgpd' && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
            <div style={{ width: 34, height: 34, borderRadius: 10, background: '#EFF6FF', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <Icon name="map" size={17} color={DS.colors.primary} />
            </div>
            <div style={{ fontSize: 13, color: DS.colors.textSec, lineHeight: 1.6 }}>
              Para validar sua presença nas ações, o Promo Up usa a localização
              do aparelho <b>somente</b> no check-in, no envio de registros e no
              check-out.
              <br /><br />
              Não há rastreamento fora das ações.
            </div>
          </div>
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            <Btn variant="secondary" onClick={onClose}>Agora não</Btn>
            <Btn icon="check" onClick={aceitarLgpd}>Aceitar e continuar</Btn>
          </div>
        </div>
      )}

      {etapa === 'localizando' && (
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, padding: '18px 0' }}>
          <Spinner />
          <div style={{ fontSize: 13, fontWeight: 700, color: DS.colors.text }}>Localizando você…</div>
          <div style={{ fontSize: 12, color: DS.colors.textMuted, textAlign: 'center' }}>
            Se o navegador pedir permissão, toque em <b>Permitir</b>.
          </div>
        </div>
      )}

      {etapa === 'erro' && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
          <div style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
            <div style={{ width: 34, height: 34, borderRadius: 10, background: '#FEF2F2', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <Icon name="alert" size={17} color={DS.colors.danger} />
            </div>
            <div style={{ fontSize: 13, color: DS.colors.text, lineHeight: 1.6 }}>{erro}</div>
          </div>
          {distancia !== null && (
            <div style={{ fontSize: 12, color: DS.colors.textMuted, textAlign: 'center' }}>
              Distância medida agora: <b>{fmtDistancia(distancia)}</b> · limite {raio} m
            </div>
          )}
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            <Btn variant="secondary" onClick={onClose}>Fechar</Btn>
            <Btn icon="check" onClick={localizar}>Tentar de novo</Btn>
          </div>
        </div>
      )}

      {(etapa === 'foto' || etapa === 'enviando') && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div style={{
            display: 'flex', alignItems: 'center', gap: 10,
            background: '#F0FDF4', border: '1px solid #BBF7D0',
            borderRadius: DS.radius.sm, padding: '10px 12px',
          }}>
            <Icon name="check" size={16} color={DS.colors.success} />
            <div style={{ minWidth: 0 }}>
              <div style={{ fontSize: 12, fontWeight: 800, color: '#15803D' }}>
                Localização confirmada
                {distancia !== null && ' · ' + fmtDistancia(distancia) + ' do local'}
              </div>
              {endereco && (
                <div style={{ fontSize: 11, color: DS.colors.textSec, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                  {endereco}
                </div>
              )}
            </div>
          </div>

          {pos && pos.precisao > GEO_PRECISAO_RUIM_M && (
            <div style={{ fontSize: 11, color: DS.colors.warning, lineHeight: 1.5 }}>
              O sinal de GPS está fraco (precisão de {Math.round(pos.precisao)} m).
              Se possível, saia de baixo de cobertura antes de continuar.
            </div>
          )}

          <div>
            <div style={{ fontSize: 12, fontWeight: 700, color: DS.colors.textSec, marginBottom: 8 }}>
              Foto de chegada <span style={{ color: DS.colors.danger }}>*</span>
            </div>
            <label style={{
              display: 'block', borderRadius: DS.radius.sm, overflow: 'hidden',
              border: '1px dashed ' + (preview ? 'transparent' : DS.colors.border),
              cursor: etapa === 'enviando' ? 'default' : 'pointer',
            }}>
              {preview ? (
                <img src={preview} alt="Foto de chegada"
                  style={{ width: '100%', maxHeight: 260, objectFit: 'cover', display: 'block' }} />
              ) : (
                <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, padding: '26px 12px', color: DS.colors.textSec }}>
                  <Icon name="photo" size={22} color={DS.colors.textMuted} />
                  <div style={{ fontSize: 13, fontWeight: 700 }}>Tirar foto agora</div>
                  <div style={{ fontSize: 11, color: DS.colors.textMuted }}>Selfie ou foto do ponto da ação</div>
                </div>
              )}
              <input type="file" accept="image/*" capture="environment" disabled={etapa === 'enviando'}
                onChange={e => { escolherFoto(e.target.files && e.target.files[0]); e.target.value = ''; }}
                style={{ display: 'none' }} />
            </label>
            {preview && etapa !== 'enviando' && (
              <div style={{ fontSize: 11, color: DS.colors.textMuted, marginTop: 6, textAlign: 'center' }}>
                Toque na imagem para tirar outra
              </div>
            )}
          </div>

          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            <Btn variant="secondary" onClick={onClose} disabled={etapa === 'enviando'}>Cancelar</Btn>
            <Btn icon="check" onClick={confirmar} disabled={!arquivo || etapa === 'enviando'}>
              {etapa === 'enviando' ? 'Enviando…' : 'Confirmar check-in'}
            </Btn>
          </div>
        </div>
      )}
    </Modal>
  );
}

Object.assign(window, {
  CheckInModal, obterPosicao, distanciaMetros, enderecoAproximado, fmtDistancia,
  GEO_PRECISAO_RUIM_M,
});
