---
- name: Validate required dns_record variables
  ansible.builtin.assert:
    that:
      - dns_record_subdomain is defined
      - dns_record_subdomain | length > 0
      - dns_record_domain is defined
      - dns_record_domain | length > 0
      - dns_record_cloudflare_api_token is defined
      - dns_record_cloudflare_api_token | length > 0
      - dns_record_ip is defined
      - dns_record_ip is match('^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$')
    fail_msg: >-
      dns_record requires: dns_record_subdomain, dns_record_domain,
      dns_record_cloudflare_api_token (non-empty) and dns_record_ip
      (valid IPv4, got '{{ dns_record_ip | default('undefined') }}')
  no_log: "{{ not (ansible_verbosity >= 3) }}"

- name: Look up Cloudflare zone ID for {{ dns_record_domain }}
  ansible.builtin.uri:
    url: "https://api.cloudflare.com/client/v4/zones?name={{ dns_record_domain }}"
    method: GET
    headers:
      Authorization: "Bearer {{ dns_record_cloudflare_api_token }}"
      Content-Type: application/json
    return_content: true
  register: _dns_record_zone_lookup
  failed_when: false
  no_log: true
  when: _dns_record_zone_ids is not defined or dns_record_domain not in _dns_record_zone_ids

- name: Fail if Cloudflare zone lookup failed for {{ dns_record_domain }}
  ansible.builtin.fail:
    msg: >-
      Cloudflare zone lookup failed: {{
        _dns_record_zone_lookup.json.errors
        | default(_dns_record_zone_lookup.msg | default('unknown error'))
      }} (status={{ _dns_record_zone_lookup.status | default('unknown') }})
  when:
    - _dns_record_zone_lookup is not skipped
    - not (_dns_record_zone_lookup.json.success | default(false))

- name: Fail if Cloudflare zone not found or ambiguous for {{ dns_record_domain }}
  ansible.builtin.fail:
    msg: >-
      Expected exactly 1 Cloudflare zone for '{{ dns_record_domain }}',
      got {{ _dns_record_zone_lookup.json.result | length }}.
  when:
    - _dns_record_zone_ids is not defined or dns_record_domain not in _dns_record_zone_ids
    - _dns_record_zone_lookup.json.result | length != 1

- name: Cache Cloudflare zone ID for {{ dns_record_domain }}
  ansible.builtin.set_fact:
    _dns_record_zone_ids: >-
      {{ _dns_record_zone_ids | default({}) | combine({
        dns_record_domain: _dns_record_zone_lookup.json.result[0].id
      }) }}
  when: _dns_record_zone_ids is not defined or dns_record_domain not in _dns_record_zone_ids

- name: Set zone API base URL
  ansible.builtin.set_fact:
    _dns_record_zone_url: "https://api.cloudflare.com/client/v4/zones/{{ _dns_record_zone_ids[dns_record_domain] }}"

- name: Check for existing DNS A record for {{ dns_record_subdomain + '.' + dns_record_domain }}
  ansible.builtin.uri:
    url: "{{ _dns_record_zone_url }}/dns_records?type=A&name={{ dns_record_subdomain }}.{{ dns_record_domain }}"
    method: GET
    headers:
      Authorization: "Bearer {{ dns_record_cloudflare_api_token }}"
      Content-Type: application/json
    return_content: true
  register: _dns_record_existing
  failed_when: false
  no_log: true

- name: Fail if DNS record lookup failed for {{ dns_record_subdomain + '.' + dns_record_domain }}
  ansible.builtin.fail:
    msg: >-
      DNS record lookup failed: {{
        _dns_record_existing.json.errors
        | default(_dns_record_existing.msg | default('unknown error'))
      }} (status={{ _dns_record_existing.status | default('unknown') }})
  when: not (_dns_record_existing.json.success | default(false))

- name: Fail if multiple A records found for {{ dns_record_subdomain + '.' + dns_record_domain }}
  ansible.builtin.fail:
    msg: >-
      Expected 0 or 1 A records for '{{ dns_record_subdomain }}.{{ dns_record_domain }}',
      found {{ _dns_record_existing.json.result | length }}.
      Resolve duplicates in Cloudflare dashboard before proceeding.
  when: _dns_record_existing.json.result | length > 1

- name: Create DNS A record for {{ dns_record_subdomain + '.' + dns_record_domain }}
  ansible.builtin.uri:
    url: "{{ _dns_record_zone_url }}/dns_records"
    method: POST
    headers:
      Authorization: "Bearer {{ dns_record_cloudflare_api_token }}"
      Content-Type: application/json
    body_format: json
    body:
      type: A
      name: "{{ dns_record_subdomain }}.{{ dns_record_domain }}"
      content: "{{ dns_record_ip }}"
      ttl: "{{ dns_record_ttl | int }}"
      proxied: "{{ dns_record_proxied | bool }}"
    status_code: 200
  register: _dns_record_create
  failed_when: false
  changed_when: _dns_record_create.json.success | default(false)
  when: _dns_record_existing.json.result | length == 0
  no_log: true

- name: Fail if DNS record creation failed for {{ dns_record_subdomain + '.' + dns_record_domain }}
  ansible.builtin.fail:
    msg: >-
      DNS record creation failed: {{
        _dns_record_create.json.errors
        | default(_dns_record_create.msg | default('unknown error'))
      }} (status={{ _dns_record_create.status | default('unknown') }})
  when:
    - _dns_record_create is not skipped
    - not (_dns_record_create.json.success | default(false))

- name: Update DNS A record for {{ dns_record_subdomain + '.' + dns_record_domain }}
  ansible.builtin.uri:
    url: "{{ _dns_record_zone_url }}/dns_records/{{ _dns_record_existing.json.result[0].id }}"
    method: PUT
    headers:
      Authorization: "Bearer {{ dns_record_cloudflare_api_token }}"
      Content-Type: application/json
    body_format: json
    body:
      type: A
      name: "{{ dns_record_subdomain }}.{{ dns_record_domain }}"
      content: "{{ dns_record_ip }}"
      ttl: "{{ dns_record_ttl | int }}"
      proxied: "{{ dns_record_proxied | bool }}"
    status_code: 200
  register: _dns_record_update
  failed_when: false
  changed_when: _dns_record_update.json.success | default(false)
  when:
    - _dns_record_existing.json.result | length == 1
    - >-
      _dns_record_existing.json.result[0].content != dns_record_ip or
      _dns_record_existing.json.result[0].ttl != (dns_record_ttl | int) or
      _dns_record_existing.json.result[0].proxied != (dns_record_proxied | bool)
  no_log: true

- name: Fail if DNS record update failed for {{ dns_record_subdomain + '.' + dns_record_domain }}
  ansible.builtin.fail:
    msg: >-
      DNS record update failed: {{
        _dns_record_update.json.errors
        | default(_dns_record_update.msg | default('unknown error'))
      }} (status={{ _dns_record_update.status | default('unknown') }})
  when:
    - _dns_record_update is not skipped
    - not (_dns_record_update.json.success | default(false))
