                              [table :refer [Table]])
             [metabase.util :as u]))
 
-(declare auto-assign-field-special-type-by-name!
-         mark-category-field-or-update-field-values!
+(declare mark-category-field-or-update-field-values!
          mark-json-field!
          mark-no-preview-display-field!
          mark-url-field!

      (u/try-apply sync-field! driver (assoc field :table (delay table))))))


;; ### 5) sync-table-fks!

(defn- determine-fk-type
  "Determine whether a FK is `:1t1`, or `:Mt1`.



(defn- sync-database-active-tables!
  "Perform sync operations for the given list of tables.  We do 2 passes over the listed tables, the first which does
   the bulk of the work and establishes fields & metadata, and a second pass to fill in foreign keys if supported."
  [driver active-tables & {:keys [analyze?]
                           :or {analyze? true}}]
  ;; Do a first pass which does the bulk of the work
  (let [tables-count          (count active-tables)
        finished-tables-count (atom 0)]
    (doseq [table active-tables]
      (sync-database-active-table! driver table :analyze? analyze?)

      (swap! finished-tables-count inc)
      (log/debug (u/format-color 'magenta "%s Synced table '%s'." (sync-progress-meter-string @finished-tables-count tables-count) (:name table)))))

  ;; Second pass to sync FKs, which must take place after all other table info is in place
  (when (contains? (driver/features driver) :foreign-keys)
    (doseq [table active-tables]
      (u/try-apply sync-table-fks! driver table))))


(defn- sync-metabase-metadata-table!
  "Databases may include a table named `_metabase_metadata` (case-insentive) which includes descriptions or other metadata about the `Tables` and `Fields`
   it contains. This table is *not* synced normally, i.e. a Metabase `Table` is not created for it. Instead, *this* function is called, which reads the data it
   contains and updates the relevant Metabase objects.

   The table should have the following schema:

     column  | type    | example
     --------+---------+-------------------------------------------------
     keypath | varchar | \"products.created_at.description\"
     value   | varchar | \"The date the product was added to our catalog.\"

   `keypath` is of the form `table-name.key` or `table-name.field-name.key`, where `key` is the name of some property of `Table` or `Field`.

   This functionality is currently only used by the Sample Dataset. In order to use this functionality, drivers must implement optional fn `:table-rows-seq`."
  [driver database _metabase_metadata]
  (doseq [{:keys [keypath value]} (driver/table-rows-seq driver database (:name _metabase_metadata))]
    (let [[_ table-name field-name k] (re-matches #"^([^.]+)\.(?:([^.]+)\.)?([^.]+)$" keypath)]
      (try (when (not= 1 (if field-name
                           (k/update Field
                                     (k/where {:name field-name, :table_id (k/subselect Table
                                                                                        (k/fields :id)
                                                                                        (k/where {:db_id (:id database), :name table-name}))})
                                     (k/set-fields {(keyword k) value}))
                           (k/update Table
                                     (k/where {:name table-name, :db_id (:id database)})
                                     (k/set-fields {(keyword k) value}))))
             (log/error (u/format-color "Error syncing _metabase_metadata: no matching keypath: %s" keypath)))
           (catch Throwable e
             (log/error (u/format-color 'red "Error in _metabase_metadata: %s" (.getMessage e))))))))


(defn- sync-database-with-tracking! [driver database full-sync?]
  (let [start-time (System/currentTimeMillis)
        tracking-hash (str (java.util.UUID/randomUUID))]
    (log/info (u/format-color 'magenta "Syncing %s database '%s'..." (name driver) (:name database)))
    (events/publish-event :database-sync-begin {:database_id (:id database) :custom_id tracking-hash})

    (let [database-schema (driver/describe-database driver database)]
      ;; do some quick validations that our tables list is sensible
      (when-not (and (set? (:tables database-schema))
                     (every? map? (:tables database-schema))
                     (every? :name (:tables database-schema)))
        (throw (Exception. "Invalid results returned by describe-database. `:tables` should be a set of maps like {:name \"table_name\", :schema \"schema_name_or_nil\"}.")))

      ;; now persist the list of tables, creating new ones as needed and inactivating old ones
      (save-database-tables-list! database (:tables database-schema))

      ;; once the tables are persisted then we can do a detailed sync for each table
      (let [tables (for [table (sel :many Table, :db_id (:id database), :active true (k/order :name :ASC))]
                     ;; replace default delays with ones that reuse database (and don't require a DB call)
                     (assoc table :db (delay database)))]
        (sync-database-active-tables! driver tables :analyze? full-sync?))

      ;; lastly, if we have a _metabase_metadata table go ahead and handle it
      (when-let [_metabase_metadata (first (filter #(= (s/lower-case (:name %)) "_metabase_metadata") (:tables database-schema)))]
        (sync-metabase-metadata-table! driver database _metabase_metadata)))

    (events/publish-event :database-sync-end {:database_id (:id database) :custom_id tracking-hash :running_time (- (System/currentTimeMillis) start-time)})
    (log/info (u/format-color 'magenta "Finished syncing %s database '%s'. (%d ms)" (name driver) (:name database)
                              (- (System/currentTimeMillis) start-time)))))


;; ## sync-table steps.

;; ### 1) sync-table-active-fields-and-pks!

(def ^{:arglists '([field-def])}
infer-field-special-type
