import {EditorView, basicSetup} from "codemirror"
import {EditorState} from "@codemirror/state"
import {sql, SQLDialect, StandardSQL} from "@codemirror/lang-sql"
import {oneDark} from "@codemirror/theme-one-dark"

// Custom DuckDB dialect spec for CodeMirror
// inspired by: https://github.com/sekuel/codemirror-sql-duckdb
const DuckDBDialectSpec = {
    operatorChars: '*+-%<>!=&|^',
    charSetCasts: true,
    doubleQuotedStrings: false,
    unquotedBitLiterals: true,
    hashComments: false,
    spaceAfterDashes: false,
    specialVar: '@?',
    identifierQuotes: '"',
    // source: select string_agg(distinct keyword_name, ' ' order by keyword_name asc) from duckdb_keywords()
    keywords: 'abort absolute access action add admin after aggregate all also alter always analyse analyze and anti any array as asc asof assertion assignment asymmetric at attach attribute authorization backward before begin between bigint binary bit boolean both by cache call called cascade cascaded case cast catalog centuries century chain char character characteristics check checkpoint class close cluster coalesce collate collation column columns comment comments commit committed compression concurrently configuration conflict connection constraint constraints content continue conversion copy cost create cross csv cube current cursor cycle data database day days deallocate dec decade decades decimal declare default defaults deferrable deferred definer delete delimiter delimiters depends desc describe detach dictionary disable discard distinct do document domain double drop each else enable encoding encrypted end enum escape event except exclude excluding exclusive execute exists explain export export_state extension extensions external extract false family fetch filter first float following for force foreign forward freeze from full function functions generated glob global grant granted group grouping grouping_id groups handler having header hold hour hours identity if ignore ilike immediate immutable implicit import in include including increment index indexes inherit inherits initially inline inner inout input insensitive insert install instead int integer intersect interval into invoker is isnull isolation join json key label lambda language large last lateral leading leakproof left level like limit listen load local location lock locked logged macro map mapping match materialized maxvalue method microsecond microseconds millennia millennium millisecond milliseconds minute minutes minvalue mode month months move name names national natural nchar new next no none not nothing notify notnull nowait null nullif nulls numeric object of off offset oids old on only operator option options or order ordinality others out outer over overlaps overlay overriding owned owner parallel parser partial partition partitioned passing password percent persistent pivot pivot_longer pivot_wider placing plans policy position positional pragma preceding precision prepare prepared preserve primary prior privileges procedural procedure program publication qualify quarter quarters quote range read real reassign recheck recursive ref references referencing refresh reindex relative release rename repeatable replace replica reset respect restart restrict returning returns revoke right role rollback rollup row rows rule sample savepoint schema schemas scope scroll search second seconds secret security select semi sequence sequences serializable server session set setof sets share show similar simple skip smallint snapshot some sorted sql stable standalone start statement statistics stdin stdout storage stored strict strip struct subscription substring summarize symmetric sysid system table tables tablesample tablespace temp template temporary text then ties time timestamp to trailing transaction transform treat trigger trim true truncate trusted try_cast type types unbounded uncommitted unencrypted union unique unknown unlisten unlogged unpack unpivot until update use user using vacuum valid validate validator value values varchar variable variadic varying verbose version view views virtual volatile week weeks when where whitespace window with within without work wrapper write xml xmlattributes xmlconcat xmlelement xmlexists xmlforest xmlnamespaces xmlparse xmlpi xmlroot xmlserialize xmltable year years yes zone',
    // source: select string_agg(distinct function_name, ' ' order by function_name asc) from duckdb_functions() where function_type in ['aggregate', 'scalar', 'table']
    builtin: '!__postfix !~~ !~~* % & && * ** + - ->> / // <-> << <=> <@ >> @ @> ^ ^@ __internal_compress_integral_ubigint __internal_compress_integral_uinteger __internal_compress_integral_usmallint __internal_compress_integral_utinyint __internal_compress_string_ubigint __internal_compress_string_uhugeint __internal_compress_string_uinteger __internal_compress_string_usmallint __internal_compress_string_utinyint __internal_decompress_integral_bigint __internal_decompress_integral_hugeint __internal_decompress_integral_integer __internal_decompress_integral_smallint __internal_decompress_integral_ubigint __internal_decompress_integral_uhugeint __internal_decompress_integral_uinteger __internal_decompress_integral_usmallint __internal_decompress_string abs acos acosh add age aggregate alias any_value apply approx_count_distinct approx_quantile approx_top_k arbitrary arg_max arg_max_null arg_min arg_min_null argmax argmin array_agg array_aggr array_aggregate array_apply array_cat array_concat array_contains array_cosine_distance array_cosine_similarity array_cross_product array_distance array_distinct array_dot_product array_extract array_filter array_grade_up array_has array_has_all array_has_any array_indexof array_inner_product array_length array_negative_dot_product array_negative_inner_product array_position array_reduce array_resize array_reverse_sort array_select array_slice array_sort array_to_json array_transform array_unique array_value array_where array_zip arrow_scan arrow_scan_dumb ascii asin asinh atan atan2 atanh avg bar base64 bin bit_and bit_count bit_length bit_or bit_position bit_xor bitstring bitstring_agg bool_and bool_or can_cast_implicitly cardinality cast_to_type cbrt ceil ceiling century char_length character_length checkpoint chr combine concat concat_ws constant_or_null contains corr cos cosh cot count count_if count_star countif covar_pop covar_samp create_sort_key current_connection_id current_database current_date current_localtime current_localtimestamp current_query current_query_id current_schema current_schemas current_setting current_transaction_id currval damerau_levenshtein date_diff date_part date_sub date_trunc datediff datepart datesub datetrunc day dayname dayofmonth dayofweek dayofyear decade decode degrees divide duckdb_columns duckdb_constraints duckdb_databases duckdb_dependencies duckdb_extensions duckdb_external_file_cache duckdb_functions duckdb_indexes duckdb_keywords duckdb_log_contexts duckdb_logs duckdb_memory duckdb_optimizers duckdb_prepared_statements duckdb_schemas duckdb_secret_types duckdb_secrets duckdb_sequences duckdb_settings duckdb_table_sample duckdb_tables duckdb_temporary_files duckdb_types duckdb_variables duckdb_views editdist3 element_at encode ends_with entropy enum_code enum_first enum_last enum_range enum_range_boundary epoch epoch_ms epoch_ns epoch_us equi_width_bins era error even exp factorial favg filter finalize first flatten floor force_checkpoint format formatReadableDecimalSize formatReadableSize format_bytes from_base64 from_binary from_hex from_json from_json_strict fsum gamma gcd gen_random_uuid generate_series get_bit get_current_time get_current_timestamp getvariable glob grade_up greatest greatest_common_divisor group_concat hamming hash hex histogram histogram_exact hour icu_calendar_names icu_collate_af icu_collate_am icu_collate_ar icu_collate_ar_sa icu_collate_as icu_collate_az icu_collate_be icu_collate_bg icu_collate_bn icu_collate_bo icu_collate_br icu_collate_bs icu_collate_ca icu_collate_ceb icu_collate_chr icu_collate_cs icu_collate_cy icu_collate_da icu_collate_de icu_collate_de_at icu_collate_dsb icu_collate_dz icu_collate_ee icu_collate_el icu_collate_en icu_collate_en_us icu_collate_eo icu_collate_es icu_collate_et icu_collate_fa icu_collate_fa_af icu_collate_ff icu_collate_fi icu_collate_fil icu_collate_fo icu_collate_fr icu_collate_fr_ca icu_collate_fy icu_collate_ga icu_collate_gl icu_collate_gu icu_collate_ha icu_collate_haw icu_collate_he icu_collate_he_il icu_collate_hi icu_collate_hr icu_collate_hsb icu_collate_hu icu_collate_hy icu_collate_id icu_collate_id_id icu_collate_ig icu_collate_is icu_collate_it icu_collate_ja icu_collate_ka icu_collate_kk icu_collate_kl icu_collate_km icu_collate_kn icu_collate_ko icu_collate_kok icu_collate_ku icu_collate_ky icu_collate_lb icu_collate_lkt icu_collate_ln icu_collate_lo icu_collate_lt icu_collate_lv icu_collate_mk icu_collate_ml icu_collate_mn icu_collate_mr icu_collate_ms icu_collate_mt icu_collate_my icu_collate_nb icu_collate_nb_no icu_collate_ne icu_collate_nl icu_collate_nn icu_collate_noaccent icu_collate_om icu_collate_or icu_collate_pa icu_collate_pa_in icu_collate_pl icu_collate_ps icu_collate_pt icu_collate_ro icu_collate_ru icu_collate_sa icu_collate_se icu_collate_si icu_collate_sk icu_collate_sl icu_collate_smn icu_collate_sq icu_collate_sr icu_collate_sr_ba icu_collate_sr_me icu_collate_sr_rs icu_collate_sv icu_collate_sw icu_collate_ta icu_collate_te icu_collate_th icu_collate_tk icu_collate_to icu_collate_tr icu_collate_ug icu_collate_uk icu_collate_ur icu_collate_uz icu_collate_vi icu_collate_wae icu_collate_wo icu_collate_xh icu_collate_yi icu_collate_yo icu_collate_yue icu_collate_yue_cn icu_collate_zh icu_collate_zh_cn icu_collate_zh_hk icu_collate_zh_mo icu_collate_zh_sg icu_collate_zh_tw icu_collate_zu icu_sort_key ilike_escape in_search_path instr is_histogram_other_bin isfinite isinf isnan isodow isoyear jaccard jaro_similarity jaro_winkler_similarity json_array json_array_length json_contains json_deserialize_sql json_each json_execute_serialized_sql json_exists json_extract json_extract_path json_extract_path_text json_extract_string json_keys json_merge_patch json_object json_pretty json_quote json_serialize_plan json_serialize_sql json_structure json_transform json_transform_strict json_tree json_type json_valid json_value julian kahan_sum kurtosis kurtosis_pop last last_day lcase lcm least least_common_multiple left left_grapheme len length length_grapheme levenshtein lgamma like_escape list list_aggr list_aggregate list_apply list_cat list_concat list_contains list_cosine_distance list_cosine_similarity list_distance list_distinct list_dot_product list_element list_extract list_filter list_grade_up list_has list_has_all list_has_any list_indexof list_inner_product list_negative_dot_product list_negative_inner_product list_pack list_position list_reduce list_resize list_reverse_sort list_select list_slice list_sort list_transform list_unique list_value list_where list_zip listagg ln log log10 log2 lower lpad ltrim mad make_date make_time make_timestamp make_timestamp_ns make_timestamptz map map_concat map_contains map_entries map_extract map_extract_value map_from_entries map_keys map_values max max_by md5 md5_number mean median microsecond millennium millisecond min min_by minute mismatches mod mode month monthname multiply nanosecond nextafter nextval nfc_normalize normalized_interval not_ilike_escape not_like_escape now octet_length ord pandas_scan parquet_bloom_probe parquet_file_metadata parquet_kv_metadata parquet_metadata parquet_scan parquet_schema parse_dirname parse_dirpath parse_duckdb_log_message parse_filename parse_path pg_timezone_names pi position pow power pragma_collations pragma_database_size pragma_metadata_info pragma_platform pragma_show pragma_storage_info pragma_table_info pragma_user_agent pragma_version prefix printf product python_map_function quantile quantile_cont quantile_disc quarter query query_table radians random range read_blob read_csv read_csv_auto read_json read_json_auto read_json_objects read_json_objects_auto read_ndjson read_ndjson_auto read_ndjson_objects read_parquet read_text reduce regexp_escape regexp_extract regexp_extract_all regexp_full_match regexp_matches regexp_replace regexp_split_to_array regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy remap_struct repeat repeat_row replace reservoir_quantile reverse right right_grapheme round row row_to_json rpad rtrim second sem seq_scan set_bit setseed sha1 sha256 sign signbit sin sinh skewness sniff_csv split sqrt starts_with stats stddev stddev_pop stddev_samp str_split str_split_regex strftime string_agg string_split string_split_regex string_to_array strip_accents strlen strpos strptime struct_concat struct_extract struct_extract_at struct_insert struct_pack substr substring substring_grapheme subtract suffix sum sum_no_overflow sumkahan summary tan tanh test_all_types test_vector_types time_bucket timetz_byte_comparable timezone timezone_hour timezone_minute to_base to_base64 to_binary to_centuries to_days to_decades to_hex to_hours to_json to_microseconds to_millennia to_milliseconds to_minutes to_months to_quarters to_seconds to_timestamp to_weeks to_years today transaction_timestamp translate trim trunc try_strptime txid_current typeof ucase unbin unhex unicode union_extract union_tag union_value unnest unpivot_list upper url_decode url_encode uuid uuid_extract_timestamp uuid_extract_version uuidv4 uuidv7 var_pop var_samp variance vector_type version week weekday weekofyear which_secret write_log xor year yearweek | || ~ ~~ ~~* ~~~',
    // source: select string_agg(distinct type_name, ' ' order by type_name asc) from duckdb_types()
    types: 'JSON bigint binary bit bitstring blob bool boolean bpchar bytea char date datetime dec decimal double enum float float4 float8 guid hugeint int int1 int128 int16 int2 int32 int4 int64 int8 integer integral interval list logical long map null numeric nvarchar oid real row short signed smallint string struct text time timestamp timestamp_ms timestamp_ns timestamp_s timestamp_us timestamptz timetz tinyint ubigint uhugeint uint128 uint16 uint32 uint64 uint8 uinteger union usmallint utinyint uuid varbinary varchar varint'
}
const DuckDBDialect = SQLDialect.define(DuckDBDialectSpec)

export function createEditor(textArea, options = {}) {
    // Detect if dark theme is active
    const isDarkTheme = document.documentElement.getAttribute('data-bs-theme') === 'dark'

    // Default editor content from source text area
    const defaultContent = textArea.value

    // Select proper SQL dialect
    const sqlDialect = options.dialect === 'duckdb' ? DuckDBDialect : StandardSQL

    const extensions = [
        basicSetup,
        EditorView.lineWrapping,
        sql({
            dialect: sqlDialect,
            schema: options.schema,
            upperCaseKeywords: false,
        }),
        ...(isDarkTheme ? [oneDark] : []),
    ]

    // Add readonly extension if specified
    if (options.readOnly) {
        extensions.push(EditorState.readOnly.of(true))
        extensions.push(EditorView.editable.of(false))
    } else {
        // Only add update listener if not readonly
        extensions.push(
            EditorView.updateListener.of((update) => {
                if (update.docChanged) {
                    textArea.value = editor.state.doc.toString()
                }
            })
        )
    }

    let editor = new EditorView({
        doc: defaultContent,
        extensions: extensions,
        parent: textArea.parentElement
    })

    // Initial sync (only if not readonly)
    if (!options.readOnly) {
        textArea.value = editor.state.doc.toString()
    }

    return editor
}
