/**
 * Google Sheets API v4 — read / write / append.
 *
 * 토큰은 calendar 와 공유 (같은 OAuth 에 spreadsheets scope 포함). 별도 셋업 없음 —
 * "Astra: Google Calendar OAuth 연결" 명령으로 한 번 로그인하면 둘 다 동작한다.
 *
 * 외부 라이브러리 안 씀 — Sheets API REST + native fetch.
 */

import * as vscode from 'vscode';
import { getFreshAccessToken } from '../calendar/calendarApi';

const API_BASE = 'https://sheets.googleapis.com/v4/spreadsheets';

/** 2D 값 배열 — 각 셀은 string | number | boolean (Sheets API valueType). */
export type SheetCell = string | number | boolean | null;
export type SheetValues = SheetCell[][];

export interface ReadResult { ok: true; values: SheetValues; range: string; }
export interface WriteResult { ok: true; updatedCells: number; updatedRange: string; }
export interface AppendResult extends WriteResult { appendedRange: string; }
export interface ApiFailure { ok: false; error: string; }

/**
 * range 의 셀들을 2D 배열로 읽기. 빈 셀은 빈 문자열로 패딩되지 않음 — Sheets API 는
 * 마지막 비어있지 않은 셀까지만 반환. caller 가 필요하면 normalize.
 */
export async function readSheetRange(
    context: vscode.ExtensionContext,
    spreadsheetId: string,
    range: string,
): Promise<ReadResult | ApiFailure> {
    if (!spreadsheetId.trim()) return { ok: false, error: 'spreadsheet_id 비어있음' };
    if (!range.trim()) return { ok: false, error: 'range 비어있음' };
    const tok = await getFreshAccessToken(context);
    if (!tok.ok) return { ok: false, error: tok.error };
    const url = `${API_BASE}/${encodeURIComponent(spreadsheetId)}/values/${encodeURIComponent(range)}`;
    try {
        const res = await fetch(url, {
            headers: { Authorization: `Bearer ${tok.accessToken}` },
            signal: AbortSignal.timeout(15000),
        });
        const json: any = await res.json().catch(() => ({}));
        if (!res.ok) {
            return { ok: false, error: json?.error?.message || `HTTP ${res.status}` };
        }
        return { ok: true, values: (json.values ?? []) as SheetValues, range: json.range ?? range };
    } catch (e: any) {
        return { ok: false, error: e?.message ?? String(e) };
    }
}

/**
 * range 의 셀을 values 로 덮어쓰기. range 의 좌상단부터 values 배열만큼 채움.
 * Sheets API 의 valueInputOption='USER_ENTERED' 사용 → "=A1+B1" 같은 수식은 수식으로,
 * 숫자/날짜 문자열은 Sheets 가 자동 파싱.
 */
export async function writeSheetRange(
    context: vscode.ExtensionContext,
    spreadsheetId: string,
    range: string,
    values: SheetValues,
): Promise<WriteResult | ApiFailure> {
    if (!spreadsheetId.trim()) return { ok: false, error: 'spreadsheet_id 비어있음' };
    if (!range.trim()) return { ok: false, error: 'range 비어있음' };
    if (!Array.isArray(values) || values.length === 0) return { ok: false, error: 'values 비어있음' };
    const tok = await getFreshAccessToken(context);
    if (!tok.ok) return { ok: false, error: tok.error };
    const url = `${API_BASE}/${encodeURIComponent(spreadsheetId)}/values/${encodeURIComponent(range)}` +
        `?valueInputOption=USER_ENTERED`;
    try {
        const res = await fetch(url, {
            method: 'PUT',
            headers: {
                Authorization: `Bearer ${tok.accessToken}`,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ range, values, majorDimension: 'ROWS' }),
            signal: AbortSignal.timeout(15000),
        });
        const json: any = await res.json().catch(() => ({}));
        if (!res.ok) return { ok: false, error: json?.error?.message || `HTTP ${res.status}` };
        return { ok: true, updatedCells: Number(json.updatedCells ?? 0), updatedRange: json.updatedRange ?? range };
    } catch (e: any) {
        return { ok: false, error: e?.message ?? String(e) };
    }
}

/**
 * 값 추가 — range 안에서 *가장 마지막으로 데이터가 있는 행 아래* 에 새 행으로 append.
 * 로그·일지·트래킹 시트에 유용.
 */
export async function appendSheetRows(
    context: vscode.ExtensionContext,
    spreadsheetId: string,
    range: string,
    values: SheetValues,
): Promise<AppendResult | ApiFailure> {
    if (!spreadsheetId.trim()) return { ok: false, error: 'spreadsheet_id 비어있음' };
    if (!range.trim()) return { ok: false, error: 'range 비어있음' };
    if (!Array.isArray(values) || values.length === 0) return { ok: false, error: 'values 비어있음' };
    const tok = await getFreshAccessToken(context);
    if (!tok.ok) return { ok: false, error: tok.error };
    const url = `${API_BASE}/${encodeURIComponent(spreadsheetId)}/values/${encodeURIComponent(range)}` +
        `:append?valueInputOption=USER_ENTERED&insertDataOption=INSERT_ROWS`;
    try {
        const res = await fetch(url, {
            method: 'POST',
            headers: {
                Authorization: `Bearer ${tok.accessToken}`,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ range, values, majorDimension: 'ROWS' }),
            signal: AbortSignal.timeout(15000),
        });
        const json: any = await res.json().catch(() => ({}));
        if (!res.ok) return { ok: false, error: json?.error?.message || `HTTP ${res.status}` };
        const upd = json.updates ?? {};
        return {
            ok: true,
            updatedCells: Number(upd.updatedCells ?? 0),
            updatedRange: upd.updatedRange ?? range,
            appendedRange: upd.updatedRange ?? range,
        };
    } catch (e: any) {
        return { ok: false, error: e?.message ?? String(e) };
    }
}

// ────────────── 파싱 헬퍼 — action tag 본문 TSV 해석 ──────────────

/**
 * action tag 본문(여러 줄, 탭/파이프 구분) → 2D SheetValues 변환.
 *   - 우선 탭(\t) 으로 split — 진짜 TSV. LLM 이 보통 이걸 emit.
 *   - 탭이 한 칸도 없으면 ` | ` 파이프 구분으로 fallback (양 옆 공백 1개씩).
 *   - 빈 줄은 무시 (trailing newline 안전).
 * 셀 값은 모두 문자열 — Sheets API 가 USER_ENTERED 로 자동 형변환.
 */
export function parseTsvBody(body: string): SheetValues {
    if (!body || typeof body !== 'string') return [];
    // 공백·탭·개행만 있는 입력은 빈 배열로 — LLM 이 빈 본문 emit 했을 때 안전.
    if (!body.trim()) return [];
    const trimmed = body.replace(/^\s*\n+/, '').replace(/\n+\s*$/, '');
    const lines = trimmed.split(/\r?\n/).filter((l) => l.trim().length > 0);
    if (lines.length === 0) return [];
    const useTab = lines.some((l) => l.includes('\t'));
    return lines.map((line) =>
        useTab ? line.split('\t') : line.split(/\s*\|\s*/),
    );
}

/** 결과 2D 배열을 LLM 친화적 짧은 마크다운 테이블로 (read 결과를 chat 에 주입할 때). */
export function valuesToMarkdownTable(values: SheetValues, maxRows: number = 50): string {
    if (!values.length) return '_(empty)_';
    const truncated = values.slice(0, maxRows);
    const rendered = truncated.map((row) => '| ' + row.map((c) => String(c ?? '').replace(/\|/g, '\\|')).join(' | ') + ' |');
    if (rendered.length === 0) return '_(empty)_';
    // 헤더 구분선 — 첫 행을 헤더로 가정.
    const cols = truncated[0]?.length ?? 1;
    const sep = '|' + Array(cols).fill('---').join('|') + '|';
    const result = [rendered[0], sep, ...rendered.slice(1)].join('\n');
    if (values.length > maxRows) {
        return result + `\n_(... ${values.length - maxRows} more rows truncated)_`;
    }
    return result;
}
