<cfprocessingdirective pageencoding="utf-8" />
<cfcomponent output="false">

  <cffunction name="isDbFastFailActive" access="private" returntype="boolean" output="false">
    <cfset var downUntil = "" />
    <cfif structKeyExists(application, "moduleFormServiceDbDownUntil")>
      <cfset downUntil = application.moduleFormServiceDbDownUntil />
      <cfif isDate(downUntil) AND downUntil GT now()>
        <cfreturn true />
      </cfif>
    </cfif>
    <cfreturn false />
  </cffunction>

  <cffunction name="markDbUnavailable" access="private" returntype="void" output="false">
    <cflock scope="application" type="exclusive" timeout="2">
      <cfset application.moduleFormServiceDbDownUntil = dateAdd("s", 30, now()) />
    </cflock>
  </cffunction>

  <cffunction name="markDbAvailable" access="private" returntype="void" output="false">
    <cflock scope="application" type="exclusive" timeout="2">
      <cfif structKeyExists(application, "moduleFormServiceDbDownUntil")>
        <cfset structDelete(application, "moduleFormServiceDbDownUntil") />
      </cfif>
    </cflock>
  </cffunction>

  <cffunction name="ensureStore" access="private" returntype="void" output="false">
    <cfif NOT structKeyExists(session, "moduleForms")>
      <cfset session.moduleForms = {} />
    </cfif>
  </cffunction>

  <cffunction name="buildKey" access="private" returntype="string" output="false">
    <cfargument name="companyId" type="numeric" required="true" />
    <cfargument name="plantId" type="numeric" required="true" />
    <cfargument name="moduleKey" type="string" required="true" />
    <cfargument name="reportYear" type="numeric" required="true" />
    <cfreturn arguments.companyId & "|0|" & arguments.moduleKey & "|" & arguments.reportYear />
  </cffunction>

  <cffunction name="getOrCreateForm" access="public" returntype="struct" output="false">
    <cfargument name="moduleKey" type="string" required="true" />
    <cfargument name="companyId" type="numeric" required="true" />
    <cfargument name="plantId" type="numeric" required="true" />
    <cfargument name="reportYear" type="numeric" required="true" />

    <cfset var key = "" />
    <cfset var defaults = {} />

    <cfset key = buildKey(arguments.companyId, arguments.plantId, arguments.moduleKey, arguments.reportYear) />

    <cfif arguments.moduleKey EQ "user_data">
      <cfset defaults = {
        company_name = "",
        company_suffix = "",
        upstream_operator = "",
        downstream_count = 0,
        downstream_names = "",
        sectors_contact_firstname = "",
        sectors_contact_lastname = "",
        sectors_contact_email = "",
        sectors_contact_phone = "",
        sectors_contact_comment = "",
        kio_contact_firstname = "",
        kio_contact_lastname = "",
        kio_contact_email = "",
        kio_contact_phone = "",
        kio_contact_comment = "",
        structure_contact_firstname = "",
        structure_contact_lastname = "",
        structure_contact_email = "",
        structure_contact_phone = "",
        structure_contact_comment = "",
        is_completed = 0,
        _touched = 0
      } />
    <cfelseif arguments.moduleKey EQ "gas_kio">
      <cfset defaults = {
        period_type = "calendar",
        kio_total_kwh = 0,
        kio_households_kwh = 0,
        kio_industry_kwh = 0,
        kio_services_kwh = 0,
        kio_other_kwh = 0,
        deviation_comment = "",
        is_completed = 0,
        _touched = 0
      } />
    <cfelseif arguments.moduleKey EQ "gas_structure">
      <cfset defaults = {
        period_type = "calendar",
        heating_total = 0,
        heating_private = 0,
        heating_agriculture = 0,
        heating_industry = 0,
        heating_services = 0,
        wkk_total = 0,
        wkk_private = 0,
        wkk_agriculture = 0,
        wkk_industry = 0,
        wkk_services = 0,
        gas_meters = 0,
        employees = 0,
        annual_report_print_count = 0,
        investment_total = 0,
        investment_high_pressure = 0,
        investment_mid_low = 0,
        investment_storage = 0,
        investment_other = 0,
        deviation_comment = "",
        is_completed = 0,
        _touched = 0
      } />
    </cfif>

    <cfif NOT isDbFastFailActive()>
      <cftry>
        <cfquery name="qForm" datasource="#application.datasource#">
          SELECT id, payload_json, is_completed, is_started
          FROM module_forms
          WHERE company_id = <cfqueryparam cfsqltype="cf_sql_bigint" value="#arguments.companyId#" />
            AND module_key = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.moduleKey#" />
            AND report_year = <cfqueryparam cfsqltype="cf_sql_smallint" value="#arguments.reportYear#" />
          LIMIT 1
        </cfquery>

        <cfif qForm.recordCount EQ 0>
          <cfquery datasource="#application.datasource#">
            INSERT INTO module_forms (company_id, module_key, report_year, payload_json, is_completed, is_started)
            VALUES (
              <cfqueryparam cfsqltype="cf_sql_bigint" value="#arguments.companyId#" />,
              <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.moduleKey#" />,
              <cfqueryparam cfsqltype="cf_sql_smallint" value="#arguments.reportYear#" />,
              <cfqueryparam cfsqltype="cf_sql_longvarchar" value="#serializeJSON(defaults)#" />,
              0,
              0
            )
          </cfquery>
          <cfset markDbAvailable() />
          <cfreturn defaults />
        </cfif>

        <cfset markDbAvailable() />
        <cfset dbData = deserializeJSON(qForm.payload_json[1]) />
        <cfif NOT isStruct(dbData)>
          <cfset dbData = defaults />
        </cfif>
        <cfif NOT structKeyExists(dbData, "is_completed")>
          <cfset dbData.is_completed = val(qForm.is_completed[1]) />
        </cfif>
        <cfif NOT structKeyExists(dbData, "_touched")>
          <cfset dbData._touched = val(qForm.is_started[1]) />
        </cfif>
        <cfreturn dbData />
        <cfcatch type="any">
          <cfset markDbUnavailable() />
        </cfcatch>
      </cftry>
    </cfif>

    <cfset ensureStore() />
    <cfif NOT structKeyExists(session.moduleForms, key)>
      <cfset session.moduleForms[key] = defaults />
    </cfif>
    <cfreturn session.moduleForms[key] />
  </cffunction>

  <cffunction name="saveForm" access="public" returntype="void" output="false">
    <cfargument name="moduleKey" type="string" required="true" />
    <cfargument name="companyId" type="numeric" required="true" />
    <cfargument name="plantId" type="numeric" required="true" />
    <cfargument name="reportYear" type="numeric" required="true" />
    <cfargument name="data" type="struct" required="true" />

    <cfset var key = "" />
    <cfset var payload = duplicate(arguments.data) />
    <cfset var hasUserId = false />
    <cfset var oldData = {} />
    <cfset var oldCompleted = 0 />
    <cfset payload._touched = 1 />
    <cfset payload._opened = 1 />
    <cfif NOT structKeyExists(payload, "_saved")>
      <cfset payload._saved = 1 />
    </cfif>
    <cfif NOT structKeyExists(payload, "_has_red_warnings")>
      <cfset payload._has_red_warnings = 0 />
    </cfif>
    <cfset key = buildKey(arguments.companyId, arguments.plantId, arguments.moduleKey, arguments.reportYear) />
    <cfset hasUserId = structKeyExists(session, "user") AND structKeyExists(session.user, "id") />
    <cftry>
      <cfset oldData = getOrCreateForm(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear) />
      <cfif structKeyExists(oldData, "is_completed")>
        <cfset oldCompleted = val(oldData.is_completed) />
      </cfif>
      <cfcatch>
        <cfset oldData = {} />
        <cfset oldCompleted = 0 />
      </cfcatch>
    </cftry>

    <cfif NOT isDbFastFailActive()>
      <cftry>
        <cfquery datasource="#application.datasource#">
          INSERT INTO module_forms (company_id, module_key, report_year, payload_json, is_completed, is_started, updated_by)
          VALUES (
            <cfqueryparam cfsqltype="cf_sql_bigint" value="#arguments.companyId#" />,
            <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.moduleKey#" />,
            <cfqueryparam cfsqltype="cf_sql_smallint" value="#arguments.reportYear#" />,
            <cfqueryparam cfsqltype="cf_sql_longvarchar" value="#serializeJSON(payload)#" />,
            <cfqueryparam cfsqltype="cf_sql_bit" value="#(structKeyExists(payload, "is_completed") ? val(payload.is_completed) : 0)#" />,
            1,
            <cfif hasUserId>
              <cfqueryparam cfsqltype="cf_sql_bigint" value="#session.user.id#" />
            <cfelse>
              NULL
            </cfif>
          )
          ON DUPLICATE KEY UPDATE
            payload_json = VALUES(payload_json),
            is_completed = VALUES(is_completed),
            is_started = VALUES(is_started),
            updated_by = VALUES(updated_by),
            updated_at = CURRENT_TIMESTAMP
        </cfquery>
        <cfset markDbAvailable() />
        <cfcatch type="any">
          <cfset markDbUnavailable() />
          <cfset ensureStore() />
          <cfset session.moduleForms[key] = payload />
        </cfcatch>
      </cftry>
    <cfelse>
      <cfset ensureStore() />
      <cfset session.moduleForms[key] = payload />
    </cfif>

    <cftry>
      <cfset adminSvcAudit = createObject("component", "services.AdminDataService") />
      <cfset adminSvcAudit.syncFromModuleForm(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear, payload) />
      <cfset adminSvcAudit.logAuditEvent(
        actionKey = "module_answer_saved",
        typ = "module_answer_saved",
        entityType = "module_form",
        companyId = arguments.companyId,
        reportYear = arguments.reportYear,
        details = { moduleKey = arguments.moduleKey }
      ) />
      <cfif arguments.moduleKey EQ "user_data" AND structKeyExists(session, "user") AND structKeyExists(session.user, "role") AND session.user.role EQ "supplier">
        <cfset adminSvcAudit.logAuditEvent(
          actionKey = "contact_changed_by_contact_self",
          entityType = "module_form",
          companyId = arguments.companyId,
          reportYear = arguments.reportYear,
          beforeState = oldData,
          afterState = payload,
          details = { moduleKey = arguments.moduleKey }
        ) />
      </cfif>
      <cfif oldCompleted NEQ 1 AND structKeyExists(payload, "is_completed") AND val(payload.is_completed) EQ 1>
        <cfset adminSvcAudit.logAuditEvent(
          actionKey = "contact_sets_questionnaire_completed",
          entityType = "module_form",
          companyId = arguments.companyId,
          reportYear = arguments.reportYear,
          beforeState = { is_completed = oldCompleted },
          afterState = { is_completed = 1 },
          details = { moduleKey = arguments.moduleKey }
        ) />
      </cfif>
      <cfcatch>
      </cfcatch>
    </cftry>
  </cffunction>

  <cffunction name="isModuleCompleted" access="public" returntype="boolean" output="false">
    <cfargument name="moduleKey" type="string" required="true" />
    <cfargument name="companyId" type="numeric" required="true" />
    <cfargument name="plantId" type="numeric" required="true" />
    <cfargument name="reportYear" type="numeric" required="true" />

    <cfset var data = getOrCreateForm(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear) />
    <cfreturn structKeyExists(data, "is_completed") AND data.is_completed EQ 1 />
  </cffunction>

  <cffunction name="hasMeaningfulFormValues" access="private" returntype="boolean" output="false">
    <cfargument name="data" type="struct" required="true" />
    <cfset var k = "" />
    <cfset var skipKeys = "is_completed,period_type" />
    <cfloop collection="#arguments.data#" item="k">
      <cfif left(k, 1) EQ "_">
        <cfcontinue />
      </cfif>
      <cfif listFindNoCase(skipKeys, k)>
        <cfcontinue />
      </cfif>
      <cfif isNumeric(arguments.data[k]) AND val(arguments.data[k]) NEQ 0>
        <cfreturn true />
      </cfif>
      <cfif isSimpleValue(arguments.data[k]) AND len(trim(toString(arguments.data[k])))>
        <cfreturn true />
      </cfif>
    </cfloop>
    <cfreturn false />
  </cffunction>

  <cffunction name="isModuleStarted" access="public" returntype="boolean" output="false">
    <cfargument name="moduleKey" type="string" required="true" />
    <cfargument name="companyId" type="numeric" required="true" />
    <cfargument name="plantId" type="numeric" required="true" />
    <cfargument name="reportYear" type="numeric" required="true" />

    <cfset var flags = getCaptureFlags(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear) />
    <cfreturn flags.opened />
  </cffunction>

  <cffunction name="persistPayload" access="private" returntype="void" output="false">
    <cfargument name="moduleKey" type="string" required="true" />
    <cfargument name="companyId" type="numeric" required="true" />
    <cfargument name="plantId" type="numeric" required="true" />
    <cfargument name="reportYear" type="numeric" required="true" />
    <cfargument name="payload" type="struct" required="true" />
    <cfargument name="forceStarted" type="boolean" required="false" default="true" />

    <cfset var key = buildKey(arguments.companyId, arguments.plantId, arguments.moduleKey, arguments.reportYear) />
    <cfset var hasUserId = structKeyExists(session, "user") AND structKeyExists(session.user, "id") />
    <cfset var isCompleted = structKeyExists(arguments.payload, "is_completed") AND val(arguments.payload.is_completed) EQ 1 />
    <cfset var isStarted = arguments.forceStarted OR (structKeyExists(arguments.payload, "_opened") AND val(arguments.payload._opened) EQ 1) OR (structKeyExists(arguments.payload, "_touched") AND val(arguments.payload._touched) EQ 1) />

    <cfif NOT isDbFastFailActive()>
      <cftry>
        <cfquery datasource="#application.datasource#">
          INSERT INTO module_forms (company_id, module_key, report_year, payload_json, is_completed, is_started, updated_by)
          VALUES (
            <cfqueryparam cfsqltype="cf_sql_bigint" value="#arguments.companyId#" />,
            <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.moduleKey#" />,
            <cfqueryparam cfsqltype="cf_sql_smallint" value="#arguments.reportYear#" />,
            <cfqueryparam cfsqltype="cf_sql_longvarchar" value="#serializeJSON(arguments.payload)#" />,
            <cfqueryparam cfsqltype="cf_sql_bit" value="#isCompleted ? 1 : 0#" />,
            <cfqueryparam cfsqltype="cf_sql_bit" value="#isStarted ? 1 : 0#" />,
            <cfif hasUserId>
              <cfqueryparam cfsqltype="cf_sql_bigint" value="#session.user.id#" />
            <cfelse>
              NULL
            </cfif>
          )
          ON DUPLICATE KEY UPDATE
            payload_json = VALUES(payload_json),
            is_completed = VALUES(is_completed),
            is_started = VALUES(is_started),
            updated_by = VALUES(updated_by),
            updated_at = CURRENT_TIMESTAMP
        </cfquery>
        <cfset markDbAvailable() />
        <cfcatch type="any">
          <cfset markDbUnavailable() />
          <cfset ensureStore() />
          <cfset session.moduleForms[key] = arguments.payload />
        </cfcatch>
      </cftry>
    <cfelse>
      <cfset ensureStore() />
      <cfset session.moduleForms[key] = arguments.payload />
    </cfif>
  </cffunction>

  /**
   * Modul als geöffnet markieren (nach Jahreswechsel / erstem Aufruf).
   */
  <cffunction name="markModuleOpened" access="public" returntype="void" output="false">
    <cfargument name="moduleKey" type="string" required="true" />
    <cfargument name="companyId" type="numeric" required="true" />
    <cfargument name="plantId" type="numeric" required="true" />
    <cfargument name="reportYear" type="numeric" required="true" />

    <cfset var data = getOrCreateForm(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear) />
    <cfif structKeyExists(data, "is_completed") AND val(data.is_completed) EQ 1>
      <cfreturn />
    </cfif>
    <cfif structKeyExists(data, "_opened") AND val(data._opened) EQ 1>
      <cfreturn />
    </cfif>
    <cfset data._opened = 1 />
    <cfif NOT structKeyExists(data, "_saved")>
      <cfset data._saved = 0 />
    </cfif>
    <cfif NOT structKeyExists(data, "_has_red_warnings")>
      <cfset data._has_red_warnings = 0 />
    </cfif>
    <cfset persistPayload(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear, data, true) />
  </cffunction>

  /**
   * Status-Flags setzen (z. B. rote Warnungen ohne vollständigen Save).
   */
  <cffunction name="setCaptureFlags" access="public" returntype="void" output="false">
    <cfargument name="moduleKey" type="string" required="true" />
    <cfargument name="companyId" type="numeric" required="true" />
    <cfargument name="plantId" type="numeric" required="true" />
    <cfargument name="reportYear" type="numeric" required="true" />
    <cfargument name="opened" type="boolean" required="false" />
    <cfargument name="saved" type="boolean" required="false" />
    <cfargument name="hasRedWarnings" type="boolean" required="false" />

    <cfset var data = getOrCreateForm(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear) />
    <cfif structKeyExists(arguments, "opened") AND arguments.opened>
      <cfset data._opened = 1 />
    </cfif>
    <cfif structKeyExists(arguments, "saved")>
      <cfset data._saved = arguments.saved ? 1 : 0 />
      <cfif arguments.saved>
        <cfset data._opened = 1 />
        <cfset data._touched = 1 />
      </cfif>
    </cfif>
    <cfif structKeyExists(arguments, "hasRedWarnings")>
      <cfset data._has_red_warnings = arguments.hasRedWarnings ? 1 : 0 />
    </cfif>
    <cfset persistPayload(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear, data, true) />
  </cffunction>

  <cffunction name="getCaptureFlags" access="public" returntype="struct" output="false">
    <cfargument name="moduleKey" type="string" required="true" />
    <cfargument name="companyId" type="numeric" required="true" />
    <cfargument name="plantId" type="numeric" required="true" />
    <cfargument name="reportYear" type="numeric" required="true" />

    <cfset var data = {} />
    <cfset var completed = false />
    <cfset var opened = false />
    <cfset var saved = false />
    <cfset var hasRed = false />
    <cfset var meaningful = false />
    <cfset var hasOpenedKey = false />
    <cfset var hasSavedKey = false />
    <cfset var qGs = "" />

    <cftry>
      <cfset data = getOrCreateForm(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear) />
      <cfcatch>
        <cfset data = {} />
      </cfcatch>
    </cftry>

    <cfset completed = structKeyExists(data, "is_completed") AND val(data.is_completed) EQ 1 />
    <cfset meaningful = hasMeaningfulFormValues(data) />
    <cfset hasOpenedKey = structKeyExists(data, "_opened") />
    <cfset hasSavedKey = structKeyExists(data, "_saved") />
    <cfset hasRed = structKeyExists(data, "_has_red_warnings") AND val(data._has_red_warnings) EQ 1 />

    <cfif arguments.moduleKey EQ "gas_sectors">
      <cftry>
        <cfquery name="qGs" datasource="#application.datasource#">
          SELECT is_completed, total_sale_kwh, resellers_kwh, endconsumers_kwh, grosscustomers_count,
                 grosscustomers_total_kwh, sector_private_households_kwh, sector_agriculture_kwh,
                 sector_industry_kwh, sector_services_kwh, sector_district_heating_kwh, sector_other_kwh,
                 sector_fuel_kwh, sector_wkk_kwh, biogas_import_total_kwh, deviation_comment
          FROM gas_reports
          WHERE company_id = <cfqueryparam cfsqltype="cf_sql_bigint" value="#arguments.companyId#" />
            AND module_key = 'gas_sectors'
            AND report_year = <cfqueryparam cfsqltype="cf_sql_smallint" value="#arguments.reportYear#" />
          LIMIT 1
        </cfquery>
        <cfif qGs.recordCount EQ 1>
          <cfif val(qGs.is_completed[1]) EQ 1>
            <cfset completed = true />
          </cfif>
          <cfif (
            val(qGs.total_sale_kwh[1]) GT 0 OR val(qGs.resellers_kwh[1]) GT 0 OR val(qGs.endconsumers_kwh[1]) GT 0 OR
            val(qGs.grosscustomers_count[1]) GT 0 OR val(qGs.grosscustomers_total_kwh[1]) GT 0 OR
            val(qGs.sector_private_households_kwh[1]) GT 0 OR val(qGs.sector_agriculture_kwh[1]) GT 0 OR
            val(qGs.sector_industry_kwh[1]) GT 0 OR val(qGs.sector_services_kwh[1]) GT 0 OR
            val(qGs.sector_district_heating_kwh[1]) GT 0 OR val(qGs.sector_other_kwh[1]) GT 0 OR
            val(qGs.sector_fuel_kwh[1]) GT 0 OR val(qGs.sector_wkk_kwh[1]) GT 0 OR
            val(qGs.biogas_import_total_kwh[1]) GT 0 OR len(trim(toString(qGs.deviation_comment[1]))) GT 0
          )>
            <cfset meaningful = true />
          </cfif>
        </cfif>
        <cfcatch></cfcatch>
      </cftry>
    </cfif>

    <cfif hasOpenedKey>
      <cfset opened = val(data._opened) EQ 1 />
    </cfif>
    <cfif hasSavedKey>
      <cfset saved = val(data._saved) EQ 1 />
    </cfif>

    <!--- Legacy: ohne neue Flags → Daten vorhanden = geöffnet + gespeichert --->
    <cfif NOT hasOpenedKey AND NOT hasSavedKey>
      <cfif meaningful OR (structKeyExists(data, "_touched") AND val(data._touched) EQ 1)>
        <cfset opened = true />
        <cfset saved = true />
      </cfif>
    <cfelse>
      <cfif NOT opened AND (saved OR meaningful OR (structKeyExists(data, "_touched") AND val(data._touched) EQ 1))>
        <cfset opened = true />
      </cfif>
      <cfif NOT hasSavedKey AND meaningful>
        <cfset saved = true />
      </cfif>
    </cfif>

    <cfreturn {
      completed = completed,
      opened = opened,
      saved = saved,
      hasRedWarnings = hasRed,
      statusKey = resolveCaptureStatusKey(completed, opened, saved, hasRed)
    } />
  </cffunction>

  <cffunction name="resolveCaptureStatusKey" access="public" returntype="string" output="false">
    <cfargument name="completed" type="boolean" required="true" />
    <cfargument name="opened" type="boolean" required="true" />
    <cfargument name="saved" type="boolean" required="true" />
    <cfargument name="hasRedWarnings" type="boolean" required="true" />

    <cfif arguments.completed>
      <cfreturn "done" />
    </cfif>
    <cfif NOT arguments.opened>
      <cfreturn "open" />
    </cfif>
    <cfif (NOT arguments.saved) OR arguments.hasRedWarnings>
      <cfreturn "progress" />
    </cfif>
    <cfreturn "saved" />
  </cffunction>

  <cffunction name="completeModule" access="public" returntype="struct" output="false">
    <cfargument name="moduleKey" type="string" required="true" />
    <cfargument name="companyId" type="numeric" required="true" />
    <cfargument name="plantId" type="numeric" required="true" />
    <cfargument name="reportYear" type="numeric" required="true" />

    <cfset var flags = getCaptureFlags(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear) />
    <cfset var data = {} />
    <cfset var reportSvc = "" />
    <cfset var qReport = "" />
    <cfset var hasUserId = structKeyExists(session, "user") AND structKeyExists(session.user, "id") />

    <cfif flags.completed>
      <cfreturn { success = true, message = "already_completed" } />
    </cfif>
    <cfif flags.statusKey NEQ "saved">
      <cfreturn { success = false, message = "not_saved" } />
    </cfif>

    <cftry>
      <cfif arguments.moduleKey EQ "gas_sectors">
        <cfset reportSvc = createObject("component", "services.ReportService") />
        <cfset qReport = reportSvc.getOrCreateReport(arguments.companyId, arguments.reportYear, hasUserId ? session.user.id : 0) />
        <cfif qReport.recordCount NEQ 1>
          <cfreturn { success = false, message = "report_missing" } />
        </cfif>
        <cfquery datasource="#application.datasource#">
          UPDATE gas_reports
          SET is_completed = 1,
              completed_at = NOW(),
              status = 'submitted',
              updated_by = <cfif hasUserId><cfqueryparam cfsqltype="cf_sql_bigint" value="#session.user.id#" /><cfelse>NULL</cfif>
          WHERE id = <cfqueryparam cfsqltype="cf_sql_bigint" value="#val(qReport.id[1])#" />
        </cfquery>
      </cfif>

      <cfset data = getOrCreateForm(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear) />
      <cfset data.is_completed = 1 />
      <cfset data._opened = 1 />
      <cfset data._saved = 1 />
      <cfset data._touched = 1 />
      <cfset data._has_red_warnings = 0 />
      <cfset persistPayload(arguments.moduleKey, arguments.companyId, arguments.plantId, arguments.reportYear, data, true) />

      <cftry>
        <cfset createObject("component", "services.AdminDataService").logAuditEvent(
          actionKey = "contact_sets_questionnaire_completed",
          entityType = "module_form",
          companyId = arguments.companyId,
          reportYear = arguments.reportYear,
          beforeState = { is_completed = 0 },
          afterState = { is_completed = 1 },
          details = { moduleKey = arguments.moduleKey }
        ) />
        <cfcatch></cfcatch>
      </cftry>

      <cfreturn { success = true, message = "ok" } />
      <cfcatch type="any">
        <cfreturn { success = false, message = "db_error", detail = cfcatch.message } />
      </cfcatch>
    </cftry>
  </cffunction>

</cfcomponent>
